mirror of
https://github.com/lewis6991/gitsigns.nvim
synced 2025-05-16 23:19:50 +00:00
refactor: remove teal
This commit is contained in:
parent
4455bb5364
commit
4d63d996b0
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
neovim_branch: ['v0.8.3', 'nightly']
|
||||
neovim_branch: ['v0.8.3', 'v0.9.1', 'nightly']
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEOVIM_BRANCH: ${{ matrix.neovim_branch }}
|
||||
@ -56,11 +56,5 @@ jobs:
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: make test_deps
|
||||
|
||||
- name: Install Lua Deps
|
||||
run: make lua_deps
|
||||
|
||||
- name: Check lua files are built from latest teal
|
||||
run: make tl-ensure
|
||||
|
||||
- name: Run Test
|
||||
run: make test
|
||||
|
6
.stylua.toml
Normal file
6
.stylua.toml
Normal file
@ -0,0 +1,6 @@
|
||||
column_width = 100
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferSingle"
|
||||
call_parentheses = "Always"
|
@ -3,23 +3,9 @@
|
||||
- [Luarocks](https://luarocks.org/)
|
||||
- `brew install luarocks`
|
||||
|
||||
## Writing Teal
|
||||
|
||||
**Do not edit files in the lua dir**.
|
||||
|
||||
Gitsigns is implemented in teal which is essentially lua+types.
|
||||
The teal source files are generated into lua files and must be checked in together when making changes.
|
||||
CI will enforce this.
|
||||
|
||||
Once you have made changes in teal, the corresponding lua files can be built with:
|
||||
|
||||
```
|
||||
make tl-build
|
||||
```
|
||||
|
||||
## Generating docs
|
||||
|
||||
Most of the documentation is handwritten however the documentation for the configuration is generated from `teal/gitsigns/config.tl` which contains the configuration schema.
|
||||
Most of the documentation is handwritten however the documentation for the configuration is generated from `lua/gitsigns/config.lua` which contains the configuration schema.
|
||||
The documentation is generated with the lua script `gen_help.lua` which has been developed just enough to handle the current configuration schema so from time to time this script might need small improvements to handle new features but for the most part it works.
|
||||
|
||||
The documentation can be updated with:
|
||||
@ -46,40 +32,3 @@ To run the testsuite:
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
## [Diagnostic-ls](https://github.com/iamcco/diagnostic-languageserver) config for teal
|
||||
|
||||
```
|
||||
require('lspconfig').diagnosticls.setup{
|
||||
filetypes = {'teal'},
|
||||
init_options = {
|
||||
filetypes = {teal = {'tealcheck'}},
|
||||
linters = {
|
||||
tealcheck = {
|
||||
sourceName = "tealcheck",
|
||||
command = "tl",
|
||||
args = {'check', '%file'},
|
||||
isStdout = false, isStderr = true,
|
||||
rootPatterns = {"tlconfig.lua", ".git"},
|
||||
formatPattern = {
|
||||
'^([^:]+):(\\d+):(\\d+): (.+)$', {
|
||||
sourceName = 1, sourceNameFilter = true,
|
||||
line = 2, column = 3, message = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## [null-ls.nvim](https://github.com/jose-elias-alvarez/null-ls.nvim) config for teal
|
||||
|
||||
```
|
||||
local null_ls = require("null-ls")
|
||||
|
||||
null_ls.config {sources = {
|
||||
null_ls.builtins.diagnostics.teal
|
||||
}}
|
||||
require("lspconfig")["null-ls"].setup {}
|
||||
```
|
||||
|
28
Makefile
28
Makefile
@ -4,13 +4,12 @@ export PJ_ROOT=$(PWD)
|
||||
FILTER ?= .*
|
||||
|
||||
LUA_VERSION := 5.1
|
||||
TL_VERSION := 0.14.1
|
||||
NEOVIM_BRANCH ?= master
|
||||
|
||||
DEPS_DIR := $(PWD)/deps/nvim-$(NEOVIM_BRANCH)
|
||||
NVIM_DIR := $(DEPS_DIR)/neovim
|
||||
|
||||
LUAROCKS := $(DEPS_DIR)/luarocks/usr/bin/luarocks
|
||||
LUAROCKS := luarocks
|
||||
LUAROCKS_TREE := $(DEPS_DIR)/luarocks/usr
|
||||
LUAROCKS_LPATH := $(LUAROCKS_TREE)/share/lua/$(LUA_VERSION)
|
||||
LUAROCKS_INIT := eval $$($(LUAROCKS) --tree $(LUAROCKS_TREE) path) &&
|
||||
@ -26,17 +25,12 @@ $(NVIM_DIR):
|
||||
CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
CMAKE_EXTRA_FLAGS='-DCI_BUILD=OFF -DENABLE_LTO=OFF'
|
||||
|
||||
TL := $(LUAROCKS_TREE)/bin/tl
|
||||
|
||||
$(TL): $(NVIM_DIR)
|
||||
@mkdir -p $$(dirname $@)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install tl $(TL_VERSION)
|
||||
|
||||
INSPECT := $(LUAROCKS_LPATH)/inspect.lua
|
||||
|
||||
$(INSPECT): $(NVIM_DIR)
|
||||
@mkdir -p $$(dirname $@)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install inspect
|
||||
touch $@
|
||||
|
||||
LUV := $(LUAROCKS_TREE)/lib/lua/$(LUA_VERSION)/luv.so
|
||||
|
||||
@ -45,7 +39,7 @@ $(LUV): $(NVIM_DIR)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install luv
|
||||
|
||||
.PHONY: lua_deps
|
||||
lua_deps: $(TL) $(INSPECT)
|
||||
lua_deps: $(INSPECT)
|
||||
|
||||
.PHONY: test_deps
|
||||
test_deps: $(NVIM_DIR)
|
||||
@ -73,24 +67,10 @@ test: $(NVIM_DIR)
|
||||
|
||||
-@stty sane
|
||||
|
||||
.PHONY: tl-check
|
||||
tl-check: $(TL)
|
||||
$(TL) check teal/*.tl teal/**/*.tl
|
||||
|
||||
.PHONY: tl-build
|
||||
tl-build: tlconfig.lua $(TL) $(LUV)
|
||||
@$(TL) build
|
||||
@$(LUAROCKS_INIT) ./etc/add_comments.lua
|
||||
@echo Updated lua files
|
||||
|
||||
.PHONY: gen_help
|
||||
gen_help: $(INSPECT)
|
||||
@$(LUAROCKS_INIT) ./gen_help.lua
|
||||
@echo Updated help
|
||||
|
||||
.PHONY: build
|
||||
build: tl-build gen_help
|
||||
|
||||
.PHONY: tl-ensure
|
||||
tl-ensure: tl-build
|
||||
git diff --exit-code -- lua
|
||||
build: gen_help
|
||||
|
@ -5,7 +5,7 @@
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://gitter.im/gitsigns-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Super fast git decorations implemented purely in lua/teal.
|
||||
Super fast git decorations implemented purely in Lua.
|
||||
|
||||
## Preview
|
||||
|
||||
|
@ -594,11 +594,11 @@ on_attach *gitsigns-config-on_attach*
|
||||
watch_gitdir *gitsigns-config-watch_gitdir*
|
||||
Type: `table[extended]`
|
||||
Default: >
|
||||
{
|
||||
`{
|
||||
enable = true,
|
||||
interval = 1000,
|
||||
follow_files = true
|
||||
}
|
||||
follow_files = true,
|
||||
interval = 1000
|
||||
}`
|
||||
<
|
||||
When opening a file, a libuv watcher is placed on the respective
|
||||
`.git` directory to detect when changes happen to use as a trigger to
|
||||
@ -685,18 +685,9 @@ base *gitsigns-config-base*
|
||||
count_chars *gitsigns-config-count_chars*
|
||||
Type: `table`
|
||||
Default: >
|
||||
{
|
||||
[1] = '1', -- '₁',
|
||||
[2] = '2', -- '₂',
|
||||
[3] = '3', -- '₃',
|
||||
[4] = '4', -- '₄',
|
||||
[5] = '5', -- '₅',
|
||||
[6] = '6', -- '₆',
|
||||
[7] = '7', -- '₇',
|
||||
[8] = '8', -- '₈',
|
||||
[9] = '9', -- '₉',
|
||||
['+'] = '>', -- '₊',
|
||||
}
|
||||
`{ "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
["+"] = ">"
|
||||
}`
|
||||
<
|
||||
The count characters used when `signs.*.show_count` is enabled. The
|
||||
`+` entry is used as a fallback. With the default, any count outside
|
||||
@ -728,13 +719,13 @@ max_file_length *gitsigns-config-max_file_length*
|
||||
preview_config *gitsigns-config-preview_config*
|
||||
Type: `table[extended]`
|
||||
Default: >
|
||||
{
|
||||
border = 'single',
|
||||
style = 'minimal',
|
||||
relative = 'cursor',
|
||||
row = 0,
|
||||
col = 1
|
||||
}
|
||||
`{
|
||||
border = "single",
|
||||
col = 1,
|
||||
relative = "cursor",
|
||||
row = 0,
|
||||
style = "minimal"
|
||||
}`
|
||||
<
|
||||
Option overrides for the Gitsigns preview window. Table is passed directly
|
||||
to `nvim_open_win`.
|
||||
@ -760,12 +751,12 @@ current_line_blame *gitsigns-config-current_line_blame*
|
||||
current_line_blame_opts *gitsigns-config-current_line_blame_opts*
|
||||
Type: `table[extended]`
|
||||
Default: >
|
||||
{
|
||||
`{
|
||||
delay = 1000,
|
||||
virt_text = true,
|
||||
virt_text_pos = 'eol',
|
||||
virt_text_priority = 100,
|
||||
delay = 1000
|
||||
}
|
||||
virt_text_pos = "eol",
|
||||
virt_text_priority = 100
|
||||
}`
|
||||
<
|
||||
Options for the current line blame annotation.
|
||||
|
||||
@ -792,9 +783,9 @@ current_line_blame_formatter_opts
|
||||
|
||||
Type: `table[extended]`
|
||||
Default: >
|
||||
{
|
||||
relative_time = false
|
||||
}
|
||||
`{
|
||||
relative_time = false
|
||||
}`
|
||||
<
|
||||
Options for the current line blame annotation formatter.
|
||||
|
||||
@ -802,7 +793,7 @@ current_line_blame_formatter_opts
|
||||
• relative_time: boolean
|
||||
|
||||
current_line_blame_formatter *gitsigns-config-current_line_blame_formatter*
|
||||
Type: `string|function`, Default: `' <author>, <author_time> - <summary> '`
|
||||
Type: `string|function`, Default: `" <author>, <author_time> - <summary> "`
|
||||
|
||||
String or function used to format the virtual text of
|
||||
|gitsigns-config-current_line_blame|.
|
||||
@ -880,7 +871,7 @@ current_line_blame_formatter *gitsigns-config-current_line_blame_formatter*
|
||||
|
||||
current_line_blame_formatter_nc
|
||||
*gitsigns-config-current_line_blame_formatter_nc*
|
||||
Type: `string|function`, Default: `' <author>'`
|
||||
Type: `string|function`, Default: `" <author>"`
|
||||
|
||||
String or function used to format the virtual text of
|
||||
|gitsigns-config-current_line_blame| for lines that aren't committed.
|
||||
@ -894,8 +885,12 @@ trouble *gitsigns-config-trouble*
|
||||
quickfix/location list window.
|
||||
|
||||
yadm *gitsigns-config-yadm*
|
||||
Type: `table`, Default: `{ enable = false }`
|
||||
|
||||
Type: `table`
|
||||
Default: >
|
||||
`{
|
||||
enable = false
|
||||
}`
|
||||
<
|
||||
yadm configuration.
|
||||
|
||||
word_diff *gitsigns-config-word_diff*
|
||||
|
@ -1,81 +0,0 @@
|
||||
#!/bin/sh
|
||||
_=[[
|
||||
exec luajit "$0" "$@"
|
||||
]]
|
||||
|
||||
local uv = require'luv'
|
||||
|
||||
local function read_file(path)
|
||||
local f = assert(io.open(path, 'r'))
|
||||
local t = f:read("*all")
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
local function join_paths(...)
|
||||
return table.concat({ ... }, '/'):gsub('//+', '/')
|
||||
end
|
||||
|
||||
local function dir(path)
|
||||
--- @async
|
||||
return coroutine.wrap(function()
|
||||
local dirs = { { path, 1 } }
|
||||
while #dirs > 0 do
|
||||
local dir0, level = unpack(table.remove(dirs, 1))
|
||||
local dir1 = level == 1 and dir0 or join_paths(path, dir0)
|
||||
local fs = uv.fs_scandir(dir1)
|
||||
while fs do
|
||||
local name, t = uv.fs_scandir_next(fs)
|
||||
if not name then
|
||||
break
|
||||
end
|
||||
local f = level == 1 and name or join_paths(dir0, name)
|
||||
if t == 'directory' then
|
||||
dirs[#dirs + 1] = { f, level + 1 }
|
||||
else
|
||||
coroutine.yield(f, t)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function write_file(path, lines)
|
||||
local f = assert(io.open(path, 'w'))
|
||||
f:write(table.concat(lines, '\n'))
|
||||
f:close()
|
||||
end
|
||||
|
||||
local function read_file_lines(path)
|
||||
local lines = {}
|
||||
for l in read_file(path):gmatch("([^\n]*)\n?") do
|
||||
table.insert(lines, l)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
for p in dir('teal') do
|
||||
local path = join_paths('teal', p)
|
||||
local op = p:gsub('%.tl$', '.lua')
|
||||
local opath = join_paths('lua', op)
|
||||
|
||||
local lines = read_file_lines(path)
|
||||
|
||||
local comments = {}
|
||||
for i, l in ipairs(lines) do
|
||||
local comment = l:match('%s*%-%-.*')
|
||||
if comment then
|
||||
comments[i] = comment:gsub(' ', ' ')
|
||||
end
|
||||
end
|
||||
|
||||
local olines = read_file_lines(opath)
|
||||
|
||||
for i, l in pairs(comments) do
|
||||
if not olines[i]:match('%-%-.*') then
|
||||
olines[i] = olines[i]..l
|
||||
end
|
||||
end
|
||||
write_file(opath, olines)
|
||||
end
|
||||
|
114
gen_help.lua
114
gen_help.lua
@ -15,10 +15,6 @@ function table.slice(tbl, first, last, step)
|
||||
return sliced
|
||||
end
|
||||
|
||||
local function is_simple_type(t)
|
||||
return t == 'number' or t == 'string' or t == 'boolean'
|
||||
end
|
||||
|
||||
local function startswith(str, start)
|
||||
return str.sub(str, 1, string.len(start)) == start
|
||||
end
|
||||
@ -30,18 +26,11 @@ local function read_file(path)
|
||||
return t
|
||||
end
|
||||
|
||||
local function read_file_lines(path)
|
||||
local lines = {}
|
||||
for l in read_file(path):gmatch("([^\n]*)\n?") do
|
||||
table.insert(lines, l)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
-- To make sure the output is consistent between runs (to minimise diffs), we
|
||||
-- need to iterate through the schema keys in a deterministic way. To do this we
|
||||
-- do a smple scan over the file the schema is defined in and collect the keys
|
||||
-- in the order they are defined.
|
||||
--- @return string[]
|
||||
local function get_ordered_schema_keys()
|
||||
local c = read_file('lua/gitsigns/config.lua')
|
||||
|
||||
@ -58,7 +47,7 @@ local function get_ordered_schema_keys()
|
||||
if startswith(l, '}') then
|
||||
break
|
||||
end
|
||||
if l:find('^ (%w+).*') then
|
||||
if l:find('^ (%w+).*') then
|
||||
local lc = l:gsub('^%s*([%w_]+).*', '%1')
|
||||
table.insert(keys, lc)
|
||||
end
|
||||
@ -67,52 +56,6 @@ local function get_ordered_schema_keys()
|
||||
return keys
|
||||
end
|
||||
|
||||
local function get_default(field)
|
||||
local cfg = read_file_lines('teal/gitsigns/config.tl')
|
||||
|
||||
local fs, fe
|
||||
for i = 1, #cfg do
|
||||
local l = cfg[i]
|
||||
if l:match('^ '..field..' =') then
|
||||
fs = i
|
||||
end
|
||||
if fs and l:match('^ }') then
|
||||
fe = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local ds, de
|
||||
for i = fs, fe do
|
||||
local l = cfg[i]
|
||||
if l:match('^ default =') then
|
||||
ds = i
|
||||
if l:match('},') or l:match('nil,') or l:match("default = '.*'") then
|
||||
de = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if ds and l:match('^ }') then
|
||||
de = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local ret = {}
|
||||
for i = ds, de do
|
||||
local l = cfg[i]
|
||||
if i == ds then
|
||||
l = l:gsub('%s*default = ', '')
|
||||
end
|
||||
if i == de then
|
||||
l = l:gsub('(.*),', '%1')
|
||||
end
|
||||
table.insert(ret, l)
|
||||
end
|
||||
|
||||
return table.concat(ret, '\n')
|
||||
end
|
||||
|
||||
local function gen_config_doc_deprecated(dep_info, out)
|
||||
if type(dep_info) == 'table' and dep_info.hard then
|
||||
out(' HARD-DEPRECATED')
|
||||
@ -153,19 +96,12 @@ local function gen_config_doc_field(field, out)
|
||||
end
|
||||
|
||||
if v.description then
|
||||
local d
|
||||
local d --- @type string
|
||||
if v.default_help ~= nil then
|
||||
d = v.default_help
|
||||
elseif is_simple_type(v.type) then
|
||||
d = inspect(v.default)
|
||||
d = ('`%s`'):format(d)
|
||||
else
|
||||
d = get_default(field)
|
||||
if d:find('\n') then
|
||||
d = d:gsub('\n([^\n\r])', '\n%1')
|
||||
else
|
||||
d = ('`%s`'):format(d)
|
||||
end
|
||||
d = inspect(v.default):gsub('\n', '\n ')
|
||||
d = ('`%s`'):format(d)
|
||||
end
|
||||
|
||||
local vtype = (function()
|
||||
@ -192,8 +128,9 @@ local function gen_config_doc_field(field, out)
|
||||
end
|
||||
end
|
||||
|
||||
--- @return string
|
||||
local function gen_config_doc()
|
||||
local res = {}
|
||||
local res = {} ---@type string[]
|
||||
local function out(line)
|
||||
res[#res+1] = line or ''
|
||||
end
|
||||
@ -203,6 +140,8 @@ local function gen_config_doc()
|
||||
return table.concat(res, '\n')
|
||||
end
|
||||
|
||||
--- @param line string
|
||||
--- @return string
|
||||
local function parse_func_header(line)
|
||||
local func = line:match('%w+%.([%w_]+)')
|
||||
if not func then
|
||||
@ -212,7 +151,7 @@ local function parse_func_header(line)
|
||||
line:match('function%((.*)%)') or -- M.name = function(args)
|
||||
line:match('function%s+%w+%.[%w_]+%((.*)%)') -- function M.name(args)
|
||||
local args = {}
|
||||
for k in string.gmatch(args_raw, "([%w_]+):") do
|
||||
for k in string.gmatch(args_raw, "([%w_]+)") do
|
||||
if k:sub(1, 1) ~= '_' then
|
||||
args[#args+1] = string.format('{%s}', k)
|
||||
end
|
||||
@ -224,6 +163,8 @@ local function parse_func_header(line)
|
||||
)
|
||||
end
|
||||
|
||||
--- @param path string
|
||||
--- @return string
|
||||
local function gen_functions_doc_from_file(path)
|
||||
local i = read_file(path):gmatch("([^\n]*)\n?")
|
||||
|
||||
@ -262,6 +203,8 @@ local function gen_functions_doc_from_file(path)
|
||||
return table.concat(res, '\n')
|
||||
end
|
||||
|
||||
--- @param files string[]
|
||||
--- @return string
|
||||
local function gen_functions_doc(files)
|
||||
local res = ''
|
||||
for _, path in ipairs(files) do
|
||||
@ -270,8 +213,9 @@ local function gen_functions_doc(files)
|
||||
return res
|
||||
end
|
||||
|
||||
--- @return string
|
||||
local function gen_highlights_doc()
|
||||
local res = {}
|
||||
local res = {} --- @type string[]
|
||||
local highlights = require('lua.gitsigns.highlight')
|
||||
|
||||
local name_max = 0
|
||||
@ -286,12 +230,11 @@ local function gen_highlights_doc()
|
||||
for _, hl in ipairs(highlights.hls) do
|
||||
for name, spec in pairs(hl) do
|
||||
if not spec.hidden then
|
||||
local fallbacks_tbl = {}
|
||||
local fallbacks_tbl = {} --- @type string[]
|
||||
for _, f in ipairs(spec) do
|
||||
fallbacks_tbl[#fallbacks_tbl+1] = string.format('`%s`', f)
|
||||
end
|
||||
local fallbacks = table.concat(fallbacks_tbl, ', ')
|
||||
local pad = string.rep(' ', name_max - name:len())
|
||||
res[#res+1] = string.format('%s*hl-%s*', string.rep(' ', 56), name)
|
||||
res[#res+1] = string.format('%s', name)
|
||||
if spec.desc then
|
||||
@ -306,11 +249,12 @@ local function gen_highlights_doc()
|
||||
return table.concat(res, '\n')
|
||||
end
|
||||
|
||||
--- @return string
|
||||
local function get_setup_from_readme()
|
||||
local i = read_file('README.md'):gmatch("([^\n]*)\n?")
|
||||
local res = {}
|
||||
local res = {} --- @type string[]
|
||||
local function append(line)
|
||||
res[#res+1] = line ~= '' and ' '..line or ''
|
||||
res[#res+1] = line ~= '' and ' '..line or ''
|
||||
end
|
||||
for l in i do
|
||||
if l:match("require%('gitsigns'%).setup {") then
|
||||
@ -332,14 +276,16 @@ end
|
||||
local function get_marker_text(marker)
|
||||
return ({
|
||||
VERSION = '0.7-dev',
|
||||
CONFIG = gen_config_doc,
|
||||
FUNCTIONS = gen_functions_doc{
|
||||
'teal/gitsigns.tl',
|
||||
'teal/gitsigns/attach.tl',
|
||||
'teal/gitsigns/actions.tl',
|
||||
},
|
||||
HIGHLIGHTS = gen_highlights_doc,
|
||||
SETUP = get_setup_from_readme
|
||||
CONFIG = function() return gen_config_doc() end,
|
||||
FUNCTIONS = function()
|
||||
return gen_functions_doc{
|
||||
'lua/gitsigns.lua',
|
||||
'lua/gitsigns/attach.lua',
|
||||
'lua/gitsigns/actions.lua',
|
||||
}
|
||||
end,
|
||||
HIGHLIGHTS = function() return gen_highlights_doc() end,
|
||||
SETUP = function() return get_setup_from_readme() end,
|
||||
})[marker]
|
||||
end
|
||||
|
||||
|
@ -7,7 +7,7 @@ version = _MODREV .. _SPECREV
|
||||
description = {
|
||||
summary = 'Git signs written in pure lua',
|
||||
detailed = [[
|
||||
Super fast git decorations implemented purely in lua/teal.
|
||||
Super fast git decorations implemented purely in Lua.
|
||||
]],
|
||||
homepage = 'http://github.com/lewis6991/gitsigns.nvim',
|
||||
license = 'MIT/X11',
|
||||
|
3
lua/README.md
generated
3
lua/README.md
generated
@ -1,3 +0,0 @@
|
||||
**WARNING**: Do not edit the files in this directory. The files are generated from [teal](https://github.com/teal-language/tl). For the original source files please look in the [teal](../teal) directory.
|
||||
|
||||
See [Makefile](../Makefile) for targets on handling the teal files.
|
252
lua/gitsigns.lua
generated
252
lua/gitsigns.lua
generated
@ -2,7 +2,6 @@ local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local Config = gs_config.Config
|
||||
local config = gs_config.config
|
||||
|
||||
local log = require('gitsigns.debug.log')
|
||||
@ -14,193 +13,186 @@ local uv = require('gitsigns.uv')
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
-- from attach.tl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local cwd_watcher
|
||||
|
||||
local update_cwd_head = void(function()
|
||||
local paths = vim.fs.find('.git', {
|
||||
limit = 1,
|
||||
upward = true,
|
||||
type = 'directory',
|
||||
})
|
||||
local paths = vim.fs.find('.git', {
|
||||
limit = 1,
|
||||
upward = true,
|
||||
type = 'directory',
|
||||
})
|
||||
|
||||
if #paths == 0 then
|
||||
return
|
||||
end
|
||||
if #paths == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
|
||||
-- Look in the cache first
|
||||
for _, bcache in pairs(gs_cache.cache) do
|
||||
local repo = bcache.git_obj.repo
|
||||
if repo.toplevel == cwd then
|
||||
head = repo.abbrev_head
|
||||
gitdir = repo.gitdir
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Look in the cache first
|
||||
for _, bcache in pairs(gs_cache.cache) do
|
||||
local repo = bcache.git_obj.repo
|
||||
if repo.toplevel == cwd then
|
||||
head = repo.abbrev_head
|
||||
gitdir = repo.gitdir
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local git = require('gitsigns.git')
|
||||
local git = require('gitsigns.git')
|
||||
|
||||
if not head or not gitdir then
|
||||
local info = git.get_repo_info(cwd)
|
||||
gitdir = info.gitdir
|
||||
head = info.abbrev_head
|
||||
end
|
||||
if not head or not gitdir then
|
||||
local info = git.get_repo_info(cwd)
|
||||
gitdir = info.gitdir
|
||||
head = info.abbrev_head
|
||||
end
|
||||
|
||||
scheduler()
|
||||
vim.g.gitsigns_head = head
|
||||
scheduler()
|
||||
vim.g.gitsigns_head = head
|
||||
|
||||
if not gitdir then
|
||||
return
|
||||
end
|
||||
if not gitdir then
|
||||
return
|
||||
end
|
||||
|
||||
local towatch = gitdir .. '/HEAD'
|
||||
local towatch = gitdir .. '/HEAD'
|
||||
|
||||
if cwd_watcher:getpath() == towatch then
|
||||
-- Already watching
|
||||
return
|
||||
end
|
||||
if cwd_watcher:getpath() == towatch then
|
||||
-- Already watching
|
||||
return
|
||||
end
|
||||
|
||||
-- Watch .git/HEAD to detect branch changes
|
||||
cwd_watcher:start(
|
||||
towatch,
|
||||
config.watch_gitdir.interval,
|
||||
void(function(err)
|
||||
-- Watch .git/HEAD to detect branch changes
|
||||
cwd_watcher:start(
|
||||
towatch,
|
||||
config.watch_gitdir.interval,
|
||||
void(function(err)
|
||||
local __FUNC__ = 'cwd_watcher_cb'
|
||||
if err then
|
||||
dprintf('Git dir update error: %s', err)
|
||||
return
|
||||
dprintf('Git dir update error: %s', err)
|
||||
return
|
||||
end
|
||||
dprint('Git cwd dir update')
|
||||
|
||||
local new_head = git.get_repo_info(cwd).abbrev_head
|
||||
scheduler()
|
||||
vim.g.gitsigns_head = new_head
|
||||
end))
|
||||
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
local function setup_cli()
|
||||
api.nvim_create_user_command('Gitsigns', function(params)
|
||||
require('gitsigns.cli').run(params)
|
||||
end, {
|
||||
force = true,
|
||||
nargs = '*',
|
||||
range = true,
|
||||
complete = function(arglead, line)
|
||||
return require('gitsigns.cli').complete(arglead, line)
|
||||
end, })
|
||||
api.nvim_create_user_command('Gitsigns', function(params)
|
||||
require('gitsigns.cli').run(params)
|
||||
end, {
|
||||
force = true,
|
||||
nargs = '*',
|
||||
range = true,
|
||||
complete = function(arglead, line)
|
||||
return require('gitsigns.cli').complete(arglead, line)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local exported = {
|
||||
'attach',
|
||||
'actions',
|
||||
'attach',
|
||||
'actions',
|
||||
}
|
||||
|
||||
local function setup_debug()
|
||||
log.debug_mode = config.debug_mode
|
||||
log.verbose = config._verbose
|
||||
log.debug_mode = config.debug_mode
|
||||
log.verbose = config._verbose
|
||||
|
||||
if config.debug_mode then
|
||||
exported[#exported + 1] = 'debug'
|
||||
end
|
||||
if config.debug_mode then
|
||||
exported[#exported + 1] = 'debug'
|
||||
end
|
||||
end
|
||||
|
||||
local function setup_attach()
|
||||
scheduler()
|
||||
scheduler()
|
||||
|
||||
-- Attach to all open buffers
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if api.nvim_buf_is_loaded(buf) and
|
||||
api.nvim_buf_get_name(buf) ~= '' then
|
||||
M.attach(buf, nil, 'setup')
|
||||
scheduler()
|
||||
end
|
||||
end
|
||||
-- Attach to all open buffers
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if api.nvim_buf_is_loaded(buf) and api.nvim_buf_get_name(buf) ~= '' then
|
||||
M.attach(buf, nil, 'setup')
|
||||
scheduler()
|
||||
end
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'BufWritePost' }, {
|
||||
group = 'gitsigns',
|
||||
callback = function(data)
|
||||
M.attach(nil, nil, data.event)
|
||||
end,
|
||||
})
|
||||
api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'BufWritePost' }, {
|
||||
group = 'gitsigns',
|
||||
callback = function(data)
|
||||
M.attach(nil, nil, data.event)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function setup_cwd_head()
|
||||
scheduler()
|
||||
update_cwd_head()
|
||||
-- Need to debounce in case some plugin changes the cwd too often
|
||||
-- (like vim-grepper)
|
||||
api.nvim_create_autocmd('DirChanged', {
|
||||
group = 'gitsigns',
|
||||
callback = function()
|
||||
local debounce = require("gitsigns.debounce").debounce_trailing
|
||||
debounce(100, update_cwd_head)
|
||||
end,
|
||||
})
|
||||
scheduler()
|
||||
update_cwd_head()
|
||||
-- Need to debounce in case some plugin changes the cwd too often
|
||||
-- (like vim-grepper)
|
||||
api.nvim_create_autocmd('DirChanged', {
|
||||
group = 'gitsigns',
|
||||
callback = function()
|
||||
local debounce = require('gitsigns.debounce').debounce_trailing
|
||||
debounce(100, update_cwd_head)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
--- Setup and start Gitsigns.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {async}
|
||||
--- {async}
|
||||
---
|
||||
--- Parameters: ~
|
||||
--- {cfg} Table object containing configuration for
|
||||
--- Gitsigns. See |gitsigns-usage| for more details.
|
||||
--- {cfg} Table object containing configuration for
|
||||
--- Gitsigns. See |gitsigns-usage| for more details.
|
||||
M.setup = void(function(cfg)
|
||||
gs_config.build(cfg)
|
||||
gs_config.build(cfg)
|
||||
|
||||
if vim.fn.executable('git') == 0 then
|
||||
print('gitsigns: git not in path. Aborting setup')
|
||||
return
|
||||
end
|
||||
if vim.fn.executable('git') == 0 then
|
||||
print('gitsigns: git not in path. Aborting setup')
|
||||
return
|
||||
end
|
||||
|
||||
if config.yadm.enable and vim.fn.executable('yadm') == 0 then
|
||||
print("gitsigns: yadm not in path. Ignoring 'yadm.enable' in config")
|
||||
config.yadm.enable = false
|
||||
return
|
||||
end
|
||||
if config.yadm.enable and vim.fn.executable('yadm') == 0 then
|
||||
print("gitsigns: yadm not in path. Ignoring 'yadm.enable' in config")
|
||||
config.yadm.enable = false
|
||||
return
|
||||
end
|
||||
|
||||
setup_debug()
|
||||
setup_cli()
|
||||
setup_debug()
|
||||
setup_cli()
|
||||
|
||||
api.nvim_create_augroup('gitsigns', {})
|
||||
api.nvim_create_augroup('gitsigns', {})
|
||||
|
||||
if config._test_mode then
|
||||
require('gitsigns.attach')._setup()
|
||||
require('gitsigns.git')._set_version(config._git_version)
|
||||
end
|
||||
if config._test_mode then
|
||||
require('gitsigns.attach')._setup()
|
||||
require('gitsigns.git')._set_version(config._git_version)
|
||||
end
|
||||
|
||||
setup_attach()
|
||||
setup_cwd_head()
|
||||
setup_attach()
|
||||
setup_cwd_head()
|
||||
|
||||
M._setup_done = true
|
||||
M._setup_done = true
|
||||
end)
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(_, f)
|
||||
for _, mod in ipairs(exported) do
|
||||
local m = (require)('gitsigns.' .. mod)
|
||||
if m[f] then
|
||||
return m[f]
|
||||
end
|
||||
__index = function(_, f)
|
||||
for _, mod in ipairs(exported) do
|
||||
local m = (require)('gitsigns.' .. mod)
|
||||
if m[f] then
|
||||
return m[f]
|
||||
end
|
||||
end,
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
1632
lua/gitsigns/actions.lua
generated
1632
lua/gitsigns/actions.lua
generated
File diff suppressed because it is too large
Load Diff
219
lua/gitsigns/async.lua
generated
219
lua/gitsigns/async.lua
generated
@ -1,42 +1,15 @@
|
||||
|
||||
-- Order by highest number of return types
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Order by highest number of return types
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
local Async_T = {}
|
||||
|
||||
-- Handle for an object currently running on the event loop.
|
||||
-- The coroutine is paused while this is active.
|
||||
-- Must provide methods cancel() and is_cancelled()
|
||||
--
|
||||
-- Handle gets updated on each call to a wrapped functions, so provide access
|
||||
-- to it via a proxy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Handle for an object currently running on the event loop.
|
||||
-- The coroutine is paused while this is active.
|
||||
-- Must provide methods cancel() and is_cancelled()
|
||||
--
|
||||
-- Handle gets updated on each call to a wrapped functions, so provide access
|
||||
-- to it via a proxy
|
||||
|
||||
-- Coroutine.running() was changed between Lua 5.1 and 5.2:
|
||||
-- - 5.1: Returns the running coroutine, or nil when called by the main thread.
|
||||
@ -53,106 +26,104 @@ local handles = setmetatable({}, { __mode = 'k' })
|
||||
|
||||
--- Returns whether the current execution context is async.
|
||||
function M.running()
|
||||
local current = coroutine.running()
|
||||
if current and handles[current] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- hack: teal doesn't know table.maxn exists
|
||||
local function maxn(x)
|
||||
return ((table).maxn)(x)
|
||||
local current = coroutine.running()
|
||||
if current and handles[current] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function is_Async_T(handle)
|
||||
if handle and
|
||||
type(handle) == 'table' and
|
||||
vim.is_callable(handle.cancel) and
|
||||
vim.is_callable(handle.is_cancelled) then
|
||||
return true
|
||||
end
|
||||
if
|
||||
handle
|
||||
and type(handle) == 'table'
|
||||
and vim.is_callable(handle.cancel)
|
||||
and vim.is_callable(handle.is_cancelled)
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Analogous to uv.close
|
||||
function Async_T:cancel(cb)
|
||||
-- Cancel anything running on the event loop
|
||||
if self._current and not self._current:is_cancelled() then
|
||||
self._current:cancel(cb)
|
||||
end
|
||||
-- Cancel anything running on the event loop
|
||||
if self._current and not self._current:is_cancelled() then
|
||||
self._current:cancel(cb)
|
||||
end
|
||||
end
|
||||
|
||||
function Async_T.new(co)
|
||||
local handle = setmetatable({}, { __index = Async_T })
|
||||
handles[co] = handle
|
||||
return handle
|
||||
local handle = setmetatable({}, { __index = Async_T })
|
||||
handles[co] = handle
|
||||
return handle
|
||||
end
|
||||
|
||||
-- Analogous to uv.is_closing
|
||||
function Async_T:is_cancelled()
|
||||
return self._current and self._current:is_cancelled()
|
||||
return self._current and self._current:is_cancelled()
|
||||
end
|
||||
|
||||
local function run(func, callback, ...)
|
||||
local co = coroutine.create(func)
|
||||
local handle = Async_T.new(co)
|
||||
local co = coroutine.create(func)
|
||||
local handle = Async_T.new(co)
|
||||
|
||||
local function step(...)
|
||||
local ret = { coroutine.resume(co, ...) }
|
||||
local stat = ret[1]
|
||||
local function step(...)
|
||||
local ret = { coroutine.resume(co, ...) }
|
||||
local stat = ret[1]
|
||||
|
||||
if not stat then
|
||||
local err = ret[2]
|
||||
error(string.format("The coroutine failed with this message: %s\n%s",
|
||||
err, debug.traceback(co)))
|
||||
if not stat then
|
||||
local err = ret[2]
|
||||
error(
|
||||
string.format('The coroutine failed with this message: %s\n%s', err, debug.traceback(co))
|
||||
)
|
||||
end
|
||||
|
||||
if coroutine.status(co) == 'dead' then
|
||||
if callback then
|
||||
callback(unpack(ret, 4, table.maxn(ret)))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if coroutine.status(co) == 'dead' then
|
||||
if callback then
|
||||
callback(unpack(ret, 4, maxn(ret)))
|
||||
end
|
||||
return
|
||||
end
|
||||
local _, nargs, fn = unpack(ret)
|
||||
|
||||
local _, nargs, fn = unpack(ret)
|
||||
assert(type(fn) == 'function', 'type error :: expected func')
|
||||
|
||||
assert(type(fn) == 'function', "type error :: expected func")
|
||||
local args = { select(4, unpack(ret)) }
|
||||
args[nargs] = step
|
||||
|
||||
local args = { select(4, unpack(ret)) }
|
||||
args[nargs] = step
|
||||
local r = fn(unpack(args, 1, nargs))
|
||||
if is_Async_T(r) then
|
||||
handle._current = r
|
||||
end
|
||||
end
|
||||
|
||||
local r = fn(unpack(args, 1, nargs))
|
||||
if is_Async_T(r) then
|
||||
handle._current = r
|
||||
end
|
||||
end
|
||||
|
||||
step(...)
|
||||
return handle
|
||||
step(...)
|
||||
return handle
|
||||
end
|
||||
|
||||
function M.wait(argc, func, ...)
|
||||
-- Always run the wrapped functions in xpcall and re-raise the error in the
|
||||
-- coroutine. This makes pcall work as normal.
|
||||
local function pfunc(...)
|
||||
local args = { ... }
|
||||
local cb = args[argc]
|
||||
args[argc] = function(...)
|
||||
cb(true, ...)
|
||||
end
|
||||
xpcall(func, function(err)
|
||||
cb(false, err, debug.traceback())
|
||||
end, unpack(args, 1, argc))
|
||||
end
|
||||
-- Always run the wrapped functions in xpcall and re-raise the error in the
|
||||
-- coroutine. This makes pcall work as normal.
|
||||
local function pfunc(...)
|
||||
local args = { ... }
|
||||
local cb = args[argc]
|
||||
args[argc] = function(...)
|
||||
cb(true, ...)
|
||||
end
|
||||
xpcall(func, function(err)
|
||||
cb(false, err, debug.traceback())
|
||||
end, unpack(args, 1, argc))
|
||||
end
|
||||
|
||||
local ret = { coroutine.yield(argc, pfunc, ...) }
|
||||
local ret = { coroutine.yield(argc, pfunc, ...) }
|
||||
|
||||
local ok = ret[1]
|
||||
if not ok then
|
||||
local _, err, traceback = unpack(ret)
|
||||
error(string.format("Wrapped function failed: %s\n%s", err, traceback))
|
||||
end
|
||||
local ok = ret[1]
|
||||
if not ok then
|
||||
local _, err, traceback = unpack(ret)
|
||||
error(string.format('Wrapped function failed: %s\n%s', err, traceback))
|
||||
end
|
||||
|
||||
return unpack(ret, 2, maxn(ret))
|
||||
return unpack(ret, 2, table.maxn(ret))
|
||||
end
|
||||
|
||||
---Creates an async function with a callback style function.
|
||||
@ -160,13 +131,13 @@ end
|
||||
---@param argc number: The number of arguments of func. Must be included.
|
||||
---@return function: Returns an async function
|
||||
function M.wrap(func, argc)
|
||||
assert(argc)
|
||||
return function(...)
|
||||
if not M.running() then
|
||||
return func(...)
|
||||
end
|
||||
return M.wait(argc, func, ...)
|
||||
end
|
||||
assert(argc)
|
||||
return function(...)
|
||||
if not M.running() then
|
||||
return func(...)
|
||||
end
|
||||
return M.wait(argc, func, ...)
|
||||
end
|
||||
end
|
||||
|
||||
---Use this to create a function which executes in an async context but
|
||||
@ -174,14 +145,14 @@ end
|
||||
---since it is non-blocking
|
||||
---@param func function
|
||||
function M.create(func, argc)
|
||||
argc = argc or 0
|
||||
return function(...)
|
||||
if M.running() then
|
||||
return func(...)
|
||||
end
|
||||
local callback = select(argc + 1, ...)
|
||||
return run(func, callback, unpack({ ... }, 1, argc))
|
||||
end
|
||||
argc = argc or 0
|
||||
return function(...)
|
||||
if M.running() then
|
||||
return func(...)
|
||||
end
|
||||
local callback = select(argc + 1, ...)
|
||||
return run(func, callback, unpack({ ... }, 1, argc))
|
||||
end
|
||||
end
|
||||
|
||||
---Use this to create a function which executes in an async context but
|
||||
@ -189,12 +160,12 @@ end
|
||||
---since it is non-blocking
|
||||
---@param func function
|
||||
function M.void(func)
|
||||
return function(...)
|
||||
if M.running() then
|
||||
return func(...)
|
||||
end
|
||||
return run(func, nil, ...)
|
||||
end
|
||||
return function(...)
|
||||
if M.running() then
|
||||
return func(...)
|
||||
end
|
||||
return run(func, nil, ...)
|
||||
end
|
||||
end
|
||||
|
||||
---An async function that when called will yield to the Neovim scheduler to be
|
||||
|
551
lua/gitsigns/attach.lua
generated
551
lua/gitsigns/attach.lua
generated
@ -1,7 +1,7 @@
|
||||
local async = require('gitsigns.async')
|
||||
local git = require('gitsigns.git')
|
||||
|
||||
local log = require("gitsigns.debug.log")
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
@ -11,7 +11,7 @@ local hl = require('gitsigns.highlight')
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local CacheEntry = gs_cache.CacheEntry
|
||||
local Status = require("gitsigns.status")
|
||||
local Status = require('gitsigns.status')
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local config = gs_config.config
|
||||
@ -19,393 +19,376 @@ local config = gs_config.config
|
||||
local void = require('gitsigns.async').void
|
||||
local util = require('gitsigns.util')
|
||||
|
||||
local throttle_by_id = require("gitsigns.debounce").throttle_by_id
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local vimgrep_running = false
|
||||
|
||||
-- @return (string, string) Tuple of buffer name and commit
|
||||
local function parse_fugitive_uri(name)
|
||||
if vim.fn.exists('*FugitiveReal') == 0 then
|
||||
dprint("Fugitive not installed")
|
||||
return
|
||||
end
|
||||
if vim.fn.exists('*FugitiveReal') == 0 then
|
||||
dprint('Fugitive not installed')
|
||||
return
|
||||
end
|
||||
|
||||
local path = vim.fn.FugitiveReal(name)
|
||||
local commit = vim.fn.FugitiveParse(name)[1]:match('([^:]+):.*')
|
||||
if commit == '0' then
|
||||
-- '0' means the index so clear commit so we attach normally
|
||||
commit = nil
|
||||
end
|
||||
return path, commit
|
||||
local path = vim.fn.FugitiveReal(name)
|
||||
local commit = vim.fn.FugitiveParse(name)[1]:match('([^:]+):.*')
|
||||
if commit == '0' then
|
||||
-- '0' means the index so clear commit so we attach normally
|
||||
commit = nil
|
||||
end
|
||||
return path, commit
|
||||
end
|
||||
|
||||
local function parse_gitsigns_uri(name)
|
||||
-- TODO(lewis6991): Support submodules
|
||||
local _, _, root_path, commit, rel_path =
|
||||
name:find([[^gitsigns://(.*)/%.git/(.*):(.*)]])
|
||||
if commit == ':0' then
|
||||
-- ':0' means the index so clear commit so we attach normally
|
||||
commit = nil
|
||||
end
|
||||
if root_path then
|
||||
name = root_path .. '/' .. rel_path
|
||||
end
|
||||
return name, commit
|
||||
-- TODO(lewis6991): Support submodules
|
||||
local _, _, root_path, commit, rel_path = name:find([[^gitsigns://(.*)/%.git/(.*):(.*)]])
|
||||
if commit == ':0' then
|
||||
-- ':0' means the index so clear commit so we attach normally
|
||||
commit = nil
|
||||
end
|
||||
if root_path then
|
||||
name = root_path .. '/' .. rel_path
|
||||
end
|
||||
return name, commit
|
||||
end
|
||||
|
||||
local function get_buf_path(bufnr)
|
||||
local file =
|
||||
uv.fs_realpath(api.nvim_buf_get_name(bufnr)) or
|
||||
|
||||
api.nvim_buf_call(bufnr, function()
|
||||
local file = uv.fs_realpath(api.nvim_buf_get_name(bufnr))
|
||||
or api.nvim_buf_call(bufnr, function()
|
||||
return vim.fn.expand('%:p')
|
||||
end)
|
||||
end)
|
||||
|
||||
if not vim.wo.diff then
|
||||
if vim.startswith(file, 'fugitive://') then
|
||||
local path, commit = parse_fugitive_uri(file)
|
||||
dprintf("Fugitive buffer for file '%s' from path '%s'", path, file)
|
||||
path = uv.fs_realpath(path)
|
||||
if path then
|
||||
return path, commit
|
||||
end
|
||||
if not vim.wo.diff then
|
||||
if vim.startswith(file, 'fugitive://') then
|
||||
local path, commit = parse_fugitive_uri(file)
|
||||
dprintf("Fugitive buffer for file '%s' from path '%s'", path, file)
|
||||
path = uv.fs_realpath(path)
|
||||
if path then
|
||||
return path, commit
|
||||
end
|
||||
end
|
||||
|
||||
if vim.startswith(file, 'gitsigns://') then
|
||||
local path, commit = parse_gitsigns_uri(file)
|
||||
dprintf("Gitsigns buffer for file '%s' from path '%s'", path, file)
|
||||
path = uv.fs_realpath(path)
|
||||
if path then
|
||||
return path, commit
|
||||
end
|
||||
if vim.startswith(file, 'gitsigns://') then
|
||||
local path, commit = parse_gitsigns_uri(file)
|
||||
dprintf("Gitsigns buffer for file '%s' from path '%s'", path, file)
|
||||
path = uv.fs_realpath(path)
|
||||
if path then
|
||||
return path, commit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return file
|
||||
return file
|
||||
end
|
||||
|
||||
local function on_lines(_, bufnr, _, first, last_orig, last_new, byte_count)
|
||||
if first == last_orig and last_orig == last_new and byte_count == 0 then
|
||||
-- on_lines can be called twice for undo events; ignore the second
|
||||
-- call which indicates no changes.
|
||||
return
|
||||
end
|
||||
return manager.on_lines(bufnr, first, last_orig, last_new)
|
||||
if first == last_orig and last_orig == last_new and byte_count == 0 then
|
||||
-- on_lines can be called twice for undo events; ignore the second
|
||||
-- call which indicates no changes.
|
||||
return
|
||||
end
|
||||
return manager.on_lines(bufnr, first, last_orig, last_new)
|
||||
end
|
||||
|
||||
local function on_reload(_, bufnr)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
end
|
||||
|
||||
local function on_detach(_, bufnr)
|
||||
M.detach(bufnr, true)
|
||||
M.detach(bufnr, true)
|
||||
end
|
||||
|
||||
local function on_attach_pre(bufnr)
|
||||
local gitdir, toplevel
|
||||
if config._on_attach_pre then
|
||||
local res = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if type(res) == "table" then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir
|
||||
end
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel
|
||||
end
|
||||
local gitdir, toplevel
|
||||
if config._on_attach_pre then
|
||||
local res = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if type(res) == 'table' then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel
|
||||
end
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr, file, encoding)
|
||||
if not config.worktrees then
|
||||
return
|
||||
end
|
||||
if not config.worktrees then
|
||||
return
|
||||
end
|
||||
|
||||
for _, wt in ipairs(config.worktrees) do
|
||||
local git_obj = git.Obj.new(file, encoding, wt.gitdir, wt.toplevel)
|
||||
if git_obj and git_obj.object_name then
|
||||
dprintf('Using worktree %s', vim.inspect(wt))
|
||||
return git_obj
|
||||
end
|
||||
end
|
||||
for _, wt in ipairs(config.worktrees) do
|
||||
local git_obj = git.Obj.new(file, encoding, wt.gitdir, wt.toplevel)
|
||||
if git_obj and git_obj.object_name then
|
||||
dprintf('Using worktree %s', vim.inspect(wt))
|
||||
return git_obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local done_setup = false
|
||||
|
||||
function M._setup()
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
|
||||
done_setup = true
|
||||
done_setup = true
|
||||
|
||||
manager.setup()
|
||||
manager.setup()
|
||||
|
||||
hl.setup_highlights()
|
||||
api.nvim_create_autocmd('ColorScheme', {
|
||||
group = 'gitsigns',
|
||||
callback = hl.setup_highlights,
|
||||
})
|
||||
hl.setup_highlights()
|
||||
api.nvim_create_autocmd('ColorScheme', {
|
||||
group = 'gitsigns',
|
||||
callback = hl.setup_highlights,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('OptionSet', {
|
||||
group = 'gitsigns',
|
||||
pattern = 'fileformat',
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end, })
|
||||
api.nvim_create_autocmd('OptionSet', {
|
||||
group = 'gitsigns',
|
||||
pattern = 'fileformat',
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end,
|
||||
})
|
||||
|
||||
-- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
-- waste lots of resource and even slow down vimgrep.
|
||||
api.nvim_create_autocmd('QuickFixCmdPre', {
|
||||
group = 'gitsigns',
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = true
|
||||
end,
|
||||
})
|
||||
|
||||
-- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
-- waste lots of resource and even slow down vimgrep.
|
||||
api.nvim_create_autocmd('QuickFixCmdPre', {
|
||||
group = 'gitsigns',
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = true
|
||||
end,
|
||||
})
|
||||
api.nvim_create_autocmd('QuickFixCmdPost', {
|
||||
group = 'gitsigns',
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = false
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('QuickFixCmdPost', {
|
||||
group = 'gitsigns',
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = false
|
||||
end,
|
||||
})
|
||||
|
||||
require('gitsigns.current_line_blame').setup()
|
||||
|
||||
api.nvim_create_autocmd('VimLeavePre', {
|
||||
group = 'gitsigns',
|
||||
callback = M.detach_all,
|
||||
})
|
||||
require('gitsigns.current_line_blame').setup()
|
||||
|
||||
api.nvim_create_autocmd('VimLeavePre', {
|
||||
group = 'gitsigns',
|
||||
callback = M.detach_all,
|
||||
})
|
||||
end
|
||||
|
||||
-- Ensure attaches cannot be interleaved.
|
||||
-- Since attaches are asynchronous we need to make sure an attach isn't
|
||||
-- performed whilst another one is in progress.
|
||||
local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd)
|
||||
local __FUNC__ = 'attach'
|
||||
local __FUNC__ = 'attach'
|
||||
|
||||
M._setup()
|
||||
M._setup()
|
||||
|
||||
if vimgrep_running then
|
||||
dprint('attaching is disabled')
|
||||
if vimgrep_running then
|
||||
dprint('attaching is disabled')
|
||||
return
|
||||
end
|
||||
|
||||
if cache[cbuf] then
|
||||
dprint('Already attached')
|
||||
return
|
||||
end
|
||||
|
||||
if aucmd then
|
||||
dprintf('Attaching (trigger=%s)', aucmd)
|
||||
else
|
||||
dprint('Attaching')
|
||||
end
|
||||
|
||||
if not api.nvim_buf_is_loaded(cbuf) then
|
||||
dprint('Non-loaded buffer')
|
||||
return
|
||||
end
|
||||
|
||||
local encoding = vim.bo[cbuf].fileencoding
|
||||
if encoding == '' then
|
||||
encoding = 'utf-8'
|
||||
end
|
||||
local file
|
||||
local commit
|
||||
local gitdir_oap
|
||||
local toplevel_oap
|
||||
|
||||
if ctx then
|
||||
gitdir_oap = ctx.gitdir
|
||||
toplevel_oap = ctx.toplevel
|
||||
file = ctx.toplevel .. util.path_sep .. ctx.file
|
||||
commit = ctx.commit
|
||||
else
|
||||
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
|
||||
dprint('Exceeds max_file_length')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if cache[cbuf] then
|
||||
dprint('Already attached')
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if aucmd then
|
||||
dprintf('Attaching (trigger=%s)', aucmd)
|
||||
else
|
||||
dprint('Attaching')
|
||||
end
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
if not api.nvim_buf_is_loaded(cbuf) then
|
||||
dprint('Non-loaded buffer')
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local encoding = vim.bo[cbuf].fileencoding
|
||||
if encoding == '' then
|
||||
encoding = 'utf-8'
|
||||
end
|
||||
local file
|
||||
local commit
|
||||
local gitdir_oap
|
||||
local toplevel_oap
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
end
|
||||
|
||||
if ctx then
|
||||
gitdir_oap = ctx.gitdir
|
||||
toplevel_oap = ctx.toplevel
|
||||
file = ctx.toplevel .. util.path_sep .. ctx.file
|
||||
commit = ctx.commit
|
||||
else
|
||||
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
|
||||
dprint('Exceeds max_file_length')
|
||||
return
|
||||
end
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
async.scheduler()
|
||||
end
|
||||
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
async.scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
end
|
||||
if vim.startswith(file, repo.gitdir .. util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
return
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
if not ctx and (not util.path_exists(file) or uv.fs_stat(file).type == 'directory') then
|
||||
dprint('Not a file')
|
||||
return
|
||||
end
|
||||
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
async.scheduler()
|
||||
end
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
|
||||
async.scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
async.scheduler()
|
||||
|
||||
if vim.startswith(file, repo.gitdir .. util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
return
|
||||
end
|
||||
if config.on_attach and config.on_attach(cbuf) == false then
|
||||
dprint('User on_attach() returned false')
|
||||
return
|
||||
end
|
||||
|
||||
if not ctx and (not util.path_exists(file) or uv.fs_stat(file).type == 'directory') then
|
||||
dprint('Not a file')
|
||||
return
|
||||
end
|
||||
cache[cbuf] = CacheEntry.new({
|
||||
base = ctx and ctx.base or config.base,
|
||||
file = file,
|
||||
commit = commit,
|
||||
gitdir_watcher = manager.watch_gitdir(cbuf, repo.gitdir),
|
||||
git_obj = git_obj,
|
||||
})
|
||||
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
if not api.nvim_buf_is_loaded(cbuf) then
|
||||
dprint('Un-loaded buffer')
|
||||
return
|
||||
end
|
||||
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
-- Make sure to attach before the first update (which is async) so we pick up
|
||||
-- changes from BufReadCmd.
|
||||
api.nvim_buf_attach(cbuf, false, {
|
||||
on_lines = on_lines,
|
||||
on_reload = on_reload,
|
||||
on_detach = on_detach,
|
||||
})
|
||||
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
async.scheduler()
|
||||
-- Initial update
|
||||
manager.update(cbuf, cache[cbuf])
|
||||
|
||||
if config.on_attach and config.on_attach(cbuf) == false then
|
||||
dprint('User on_attach() returned false')
|
||||
return
|
||||
end
|
||||
|
||||
cache[cbuf] = CacheEntry.new({
|
||||
base = ctx and ctx.base or config.base,
|
||||
file = file,
|
||||
commit = commit,
|
||||
gitdir_watcher = manager.watch_gitdir(cbuf, repo.gitdir),
|
||||
git_obj = git_obj,
|
||||
})
|
||||
|
||||
if not api.nvim_buf_is_loaded(cbuf) then
|
||||
dprint('Un-loaded buffer')
|
||||
return
|
||||
end
|
||||
|
||||
-- Make sure to attach before the first update (which is async) so we pick up
|
||||
-- changes from BufReadCmd.
|
||||
api.nvim_buf_attach(cbuf, false, {
|
||||
on_lines = on_lines,
|
||||
on_reload = on_reload,
|
||||
on_detach = on_detach,
|
||||
})
|
||||
|
||||
-- Initial update
|
||||
manager.update(cbuf, cache[cbuf])
|
||||
|
||||
if config.keymaps and not vim.tbl_isempty(config.keymaps) then
|
||||
require('gitsigns.mappings')(config.keymaps, cbuf)
|
||||
end
|
||||
if config.keymaps and not vim.tbl_isempty(config.keymaps) then
|
||||
require('gitsigns.mappings')(config.keymaps, cbuf)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Detach Gitsigns from all buffers it is attached to.
|
||||
function M.detach_all()
|
||||
for k, _ in pairs(cache) do
|
||||
M.detach(k)
|
||||
end
|
||||
for k, _ in pairs(cache) do
|
||||
M.detach(k)
|
||||
end
|
||||
end
|
||||
|
||||
--- Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not
|
||||
--- provided then the current buffer is used.
|
||||
---
|
||||
--- Parameters: ~
|
||||
--- {bufnr} (number): Buffer number
|
||||
--- {bufnr} (number): Buffer number
|
||||
function M.detach(bufnr, _keep_signs)
|
||||
-- When this is called interactively (with no arguments) we want to remove all
|
||||
-- the signs, however if called via a detach event (due to nvim_buf_attach)
|
||||
-- then we don't want to clear the signs in case the buffer is just being
|
||||
-- updated due to the file externally changing. When this happens a detach and
|
||||
-- attach event happen in sequence and so we keep the old signs to stop the
|
||||
-- sign column width moving about between updates.
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
dprint('Detached')
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
dprint('Cache was nil')
|
||||
return
|
||||
end
|
||||
-- When this is called interactively (with no arguments) we want to remove all
|
||||
-- the signs, however if called via a detach event (due to nvim_buf_attach)
|
||||
-- then we don't want to clear the signs in case the buffer is just being
|
||||
-- updated due to the file externally changing. When this happens a detach and
|
||||
-- attach event happen in sequence and so we keep the old signs to stop the
|
||||
-- sign column width moving about between updates.
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
dprint('Detached')
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
dprint('Cache was nil')
|
||||
return
|
||||
end
|
||||
|
||||
manager.detach(bufnr, _keep_signs)
|
||||
manager.detach(bufnr, _keep_signs)
|
||||
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
|
||||
cache:destroy(bufnr)
|
||||
cache:destroy(bufnr)
|
||||
end
|
||||
|
||||
|
||||
--- Attach Gitsigns to the buffer.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {async}
|
||||
--- {async}
|
||||
---
|
||||
--- Parameters: ~
|
||||
--- {bufnr} (number): Buffer number
|
||||
--- {ctx} (table|nil):
|
||||
--- Git context data that may optionally be used to attach to any
|
||||
--- buffer that represents a real git object.
|
||||
--- • {file}: (string)
|
||||
--- Path to the file represented by the buffer, relative to the
|
||||
--- top-level.
|
||||
--- • {toplevel}: (string)
|
||||
--- Path to the top-level of the parent git repository.
|
||||
--- • {gitdir}: (string)
|
||||
--- Path to the git directory of the parent git repository
|
||||
--- (typically the ".git/" directory).
|
||||
--- • {commit}: (string)
|
||||
--- The git revision that the file belongs to.
|
||||
--- • {base}: (string|nil)
|
||||
--- The git revision that the file should be compared to.
|
||||
--- {bufnr} (number): Buffer number
|
||||
--- {ctx} (table|nil):
|
||||
--- Git context data that may optionally be used to attach to any
|
||||
--- buffer that represents a real git object.
|
||||
--- • {file}: (string)
|
||||
--- Path to the file represented by the buffer, relative to the
|
||||
--- top-level.
|
||||
--- • {toplevel}: (string)
|
||||
--- Path to the top-level of the parent git repository.
|
||||
--- • {gitdir}: (string)
|
||||
--- Path to the git directory of the parent git repository
|
||||
--- (typically the ".git/" directory).
|
||||
--- • {commit}: (string)
|
||||
--- The git revision that the file belongs to.
|
||||
--- • {base}: (string|nil)
|
||||
--- The git revision that the file should be compared to.
|
||||
M.attach = void(function(bufnr, ctx, _trigger)
|
||||
attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger)
|
||||
attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger)
|
||||
end)
|
||||
|
||||
return M
|
||||
|
100
lua/gitsigns/cache.lua
generated
100
lua/gitsigns/cache.lua
generated
@ -1,101 +1,67 @@
|
||||
local Hunk = require("gitsigns.hunks").Hunk
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
local GitObj = require('gitsigns.git').Obj
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
local M = {CacheEntry = {}, CacheObj = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Timer object watching the gitdir
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = { CacheEntry = {}, CacheObj = {} }
|
||||
|
||||
-- Timer object watching the gitdir
|
||||
|
||||
local CacheEntry = M.CacheEntry
|
||||
|
||||
function CacheEntry:get_compare_rev(base)
|
||||
base = base or self.base
|
||||
if base then
|
||||
return base
|
||||
end
|
||||
base = base or self.base
|
||||
if base then
|
||||
return base
|
||||
end
|
||||
|
||||
if self.commit then
|
||||
-- Buffer is a fugitive commit so compare against the parent of the commit
|
||||
if config._signs_staged_enable then
|
||||
return self.commit
|
||||
else
|
||||
return string.format('%s^', self.commit)
|
||||
end
|
||||
end
|
||||
if self.commit then
|
||||
-- Buffer is a fugitive commit so compare against the parent of the commit
|
||||
if config._signs_staged_enable then
|
||||
return self.commit
|
||||
else
|
||||
return string.format('%s^', self.commit)
|
||||
end
|
||||
end
|
||||
|
||||
local stage = self.git_obj.has_conflicts and 1 or 0
|
||||
return string.format(':%d', stage)
|
||||
local stage = self.git_obj.has_conflicts and 1 or 0
|
||||
return string.format(':%d', stage)
|
||||
end
|
||||
|
||||
function CacheEntry:get_staged_compare_rev()
|
||||
return self.commit and string.format('%s^', self.commit) or 'HEAD'
|
||||
return self.commit and string.format('%s^', self.commit) or 'HEAD'
|
||||
end
|
||||
|
||||
function CacheEntry:get_rev_bufname(rev)
|
||||
rev = rev or self:get_compare_rev()
|
||||
return string.format(
|
||||
'gitsigns://%s/%s:%s',
|
||||
self.git_obj.repo.gitdir,
|
||||
rev,
|
||||
self.git_obj.relpath)
|
||||
|
||||
rev = rev or self:get_compare_rev()
|
||||
return string.format('gitsigns://%s/%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
|
||||
end
|
||||
|
||||
function CacheEntry:invalidate()
|
||||
self.compare_text = nil
|
||||
self.compare_text_head = nil
|
||||
self.hunks = nil
|
||||
self.hunks_staged = nil
|
||||
self.compare_text = nil
|
||||
self.compare_text_head = nil
|
||||
self.hunks = nil
|
||||
self.hunks_staged = nil
|
||||
end
|
||||
|
||||
function CacheEntry.new(o)
|
||||
o.staged_diffs = o.staged_diffs or {}
|
||||
return setmetatable(o, { __index = CacheEntry })
|
||||
o.staged_diffs = o.staged_diffs or {}
|
||||
return setmetatable(o, { __index = CacheEntry })
|
||||
end
|
||||
|
||||
function CacheEntry:destroy()
|
||||
local w = self.gitdir_watcher
|
||||
if w and not w:is_closing() then
|
||||
w:close()
|
||||
end
|
||||
local w = self.gitdir_watcher
|
||||
if w and not w:is_closing() then
|
||||
w:close()
|
||||
end
|
||||
end
|
||||
|
||||
function M.CacheObj:destroy(bufnr)
|
||||
self[bufnr]:destroy()
|
||||
self[bufnr] = nil
|
||||
self[bufnr]:destroy()
|
||||
self[bufnr] = nil
|
||||
end
|
||||
|
||||
M.cache = setmetatable({}, {
|
||||
__index = M.CacheObj,
|
||||
__index = M.CacheObj,
|
||||
})
|
||||
|
||||
return M
|
||||
|
126
lua/gitsigns/cli.lua
generated
126
lua/gitsigns/cli.lua
generated
@ -12,9 +12,9 @@ local attach = require('gitsigns.attach')
|
||||
local gs_debug = require('gitsigns.debug')
|
||||
|
||||
local sources = {
|
||||
[actions] = true,
|
||||
[attach] = false,
|
||||
[gs_debug] = false,
|
||||
[actions] = true,
|
||||
[attach] = false,
|
||||
[gs_debug] = false,
|
||||
}
|
||||
|
||||
-- try to parse each argument as a lua boolean, nil or number, if fails then
|
||||
@ -25,85 +25,87 @@ local sources = {
|
||||
-- '100' -> 100
|
||||
-- 'HEAD~300' -> 'HEAD~300'
|
||||
local function parse_to_lua(a)
|
||||
if tonumber(a) then
|
||||
return tonumber(a)
|
||||
elseif a == 'false' or a == 'true' then
|
||||
return a == 'true'
|
||||
elseif a == 'nil' then
|
||||
return nil
|
||||
end
|
||||
return a
|
||||
if tonumber(a) then
|
||||
return tonumber(a)
|
||||
elseif a == 'false' or a == 'true' then
|
||||
return a == 'true'
|
||||
elseif a == 'nil' then
|
||||
return nil
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
function M.complete(arglead, line)
|
||||
local words = vim.split(line, '%s+')
|
||||
local n = #words
|
||||
local words = vim.split(line, '%s+')
|
||||
local n = #words
|
||||
|
||||
local matches = {}
|
||||
if n == 2 then
|
||||
for m, _ in pairs(sources) do
|
||||
for func, _ in pairs(m) do
|
||||
if not func:match('^[a-z]') then
|
||||
-- exclude
|
||||
elseif vim.startswith(func, arglead) then
|
||||
table.insert(matches, func)
|
||||
end
|
||||
end
|
||||
local matches = {}
|
||||
if n == 2 then
|
||||
for m, _ in pairs(sources) do
|
||||
for func, _ in pairs(m) do
|
||||
if not func:match('^[a-z]') then
|
||||
-- exclude
|
||||
elseif vim.startswith(func, arglead) then
|
||||
table.insert(matches, func)
|
||||
end
|
||||
end
|
||||
elseif n > 2 then
|
||||
-- Subcommand completion
|
||||
local cmp_func = actions._get_cmp_func(words[2])
|
||||
if cmp_func then
|
||||
return cmp_func(arglead)
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end
|
||||
elseif n > 2 then
|
||||
-- Subcommand completion
|
||||
local cmp_func = actions._get_cmp_func(words[2])
|
||||
if cmp_func then
|
||||
return cmp_func(arglead)
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end
|
||||
|
||||
local function print_nonnil(x)
|
||||
if x ~= nil then
|
||||
print(vim.inspect(x))
|
||||
end
|
||||
if x ~= nil then
|
||||
print(vim.inspect(x))
|
||||
end
|
||||
end
|
||||
|
||||
M.run = void(function(params)
|
||||
local __FUNC__ = 'cli.run'
|
||||
local pos_args_raw, named_args_raw = parse_args(params.args)
|
||||
local __FUNC__ = 'cli.run'
|
||||
local pos_args_raw, named_args_raw = parse_args(params.args)
|
||||
|
||||
local func = pos_args_raw[1]
|
||||
local func = pos_args_raw[1]
|
||||
|
||||
if not func then
|
||||
func = async.wrap(vim.ui.select, 3)(M.complete('', 'Gitsigns '), {})
|
||||
end
|
||||
if not func then
|
||||
func = async.wrap(vim.ui.select, 3)(M.complete('', 'Gitsigns '), {})
|
||||
end
|
||||
|
||||
local pos_args = vim.tbl_map(parse_to_lua, vim.list_slice(pos_args_raw, 2))
|
||||
local named_args = vim.tbl_map(parse_to_lua, named_args_raw)
|
||||
local args = vim.tbl_extend('error', pos_args, named_args)
|
||||
local pos_args = vim.tbl_map(parse_to_lua, vim.list_slice(pos_args_raw, 2))
|
||||
local named_args = vim.tbl_map(parse_to_lua, named_args_raw)
|
||||
local args = vim.tbl_extend('error', pos_args, named_args)
|
||||
|
||||
dprintf("Running action '%s' with arguments %s", func, vim.inspect(args, { newline = ' ', indent = '' }))
|
||||
dprintf(
|
||||
"Running action '%s' with arguments %s",
|
||||
func,
|
||||
vim.inspect(args, { newline = ' ', indent = '' })
|
||||
)
|
||||
|
||||
local cmd_func = actions._get_cmd_func(func)
|
||||
if cmd_func then
|
||||
-- Action has a specialised mapping function from command form to lua
|
||||
-- function
|
||||
print_nonnil(cmd_func(args, params))
|
||||
local cmd_func = actions._get_cmd_func(func)
|
||||
if cmd_func then
|
||||
-- Action has a specialised mapping function from command form to lua
|
||||
-- function
|
||||
print_nonnil(cmd_func(args, params))
|
||||
return
|
||||
end
|
||||
|
||||
for m, has_named in pairs(sources) do
|
||||
local f = (m)[func]
|
||||
if type(f) == 'function' then
|
||||
-- Note functions here do not have named arguments
|
||||
print_nonnil(f(unpack(pos_args), has_named and named_args or nil))
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for m, has_named in pairs(sources) do
|
||||
local f = (m)[func]
|
||||
if type(f) == "function" then
|
||||
-- Note functions here do not have named arguments
|
||||
print_nonnil(f(unpack(pos_args), has_named and named_args or nil))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
message.error('%s is not a valid function or action', func)
|
||||
message.error('%s is not a valid function or action', func)
|
||||
end)
|
||||
|
||||
return M
|
||||
|
186
lua/gitsigns/cli/argparse.lua
generated
186
lua/gitsigns/cli/argparse.lua
generated
@ -1,115 +1,105 @@
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
local function is_char(x)
|
||||
return x:match('[^=\'"%s]') ~= nil
|
||||
return x:match('[^=\'"%s]') ~= nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Return positional arguments and named arguments
|
||||
function M.parse_args(x)
|
||||
local pos_args, named_args = {}, {}
|
||||
local pos_args, named_args = {}, {}
|
||||
|
||||
local state = 'in_arg'
|
||||
local cur_arg = ''
|
||||
local cur_val = ''
|
||||
local cur_quote = ''
|
||||
local state = 'in_arg'
|
||||
local cur_arg = ''
|
||||
local cur_val = ''
|
||||
local cur_quote = ''
|
||||
|
||||
local function peek(idx)
|
||||
return x:sub(idx + 1, idx + 1)
|
||||
end
|
||||
local function peek(idx)
|
||||
return x:sub(idx + 1, idx + 1)
|
||||
end
|
||||
|
||||
local i = 1
|
||||
while i <= #x do
|
||||
local ch = x:sub(i, i)
|
||||
-- dprintf('L(%d)(%s): cur_arg="%s" ch="%s"', i, state, cur_arg, ch)
|
||||
local i = 1
|
||||
while i <= #x do
|
||||
local ch = x:sub(i, i)
|
||||
-- dprintf('L(%d)(%s): cur_arg="%s" ch="%s"', i, state, cur_arg, ch)
|
||||
|
||||
if state == 'in_arg' then
|
||||
if is_char(ch) then
|
||||
if ch == '-' and peek(i) == '-' then
|
||||
state = 'in_flag'
|
||||
cur_arg = ''
|
||||
i = i + 1
|
||||
else
|
||||
cur_arg = cur_arg .. ch
|
||||
end
|
||||
elseif ch:match('%s') then
|
||||
pos_args[#pos_args + 1] = cur_arg
|
||||
state = 'in_ws'
|
||||
elseif ch == '=' then
|
||||
cur_val = ''
|
||||
local next_ch = peek(i)
|
||||
if next_ch == "'" or next_ch == '"' then
|
||||
cur_quote = next_ch
|
||||
i = i + 1
|
||||
state = 'in_quote'
|
||||
else
|
||||
state = 'in_value'
|
||||
end
|
||||
end
|
||||
elseif state == 'in_flag' then
|
||||
if ch:match('%s') then
|
||||
named_args[cur_arg] = true
|
||||
state = 'in_ws'
|
||||
else
|
||||
cur_arg = cur_arg .. ch
|
||||
end
|
||||
elseif state == 'in_ws' then
|
||||
if is_char(ch) then
|
||||
if ch == '-' and peek(i) == '-' then
|
||||
state = 'in_flag'
|
||||
cur_arg = ''
|
||||
i = i + 1
|
||||
else
|
||||
state = 'in_arg'
|
||||
cur_arg = ch
|
||||
end
|
||||
end
|
||||
elseif state == 'in_value' then
|
||||
if is_char(ch) then
|
||||
cur_val = cur_val .. ch
|
||||
elseif ch:match('%s') then
|
||||
named_args[cur_arg] = cur_val
|
||||
cur_arg = ''
|
||||
state = 'in_ws'
|
||||
end
|
||||
elseif state == 'in_quote' then
|
||||
local next_ch = peek(i)
|
||||
if ch == "\\" and next_ch == cur_quote then
|
||||
cur_val = cur_val .. next_ch
|
||||
i = i + 1
|
||||
elseif ch == cur_quote then
|
||||
named_args[cur_arg] = cur_val
|
||||
state = 'in_ws'
|
||||
if next_ch ~= '' and not next_ch:match('%s') then
|
||||
error('malformed argument: ' .. next_ch)
|
||||
end
|
||||
else
|
||||
cur_val = cur_val .. ch
|
||||
end
|
||||
if state == 'in_arg' then
|
||||
if is_char(ch) then
|
||||
if ch == '-' and peek(i) == '-' then
|
||||
state = 'in_flag'
|
||||
cur_arg = ''
|
||||
i = i + 1
|
||||
else
|
||||
cur_arg = cur_arg .. ch
|
||||
end
|
||||
elseif ch:match('%s') then
|
||||
pos_args[#pos_args + 1] = cur_arg
|
||||
state = 'in_ws'
|
||||
elseif ch == '=' then
|
||||
cur_val = ''
|
||||
local next_ch = peek(i)
|
||||
if next_ch == "'" or next_ch == '"' then
|
||||
cur_quote = next_ch
|
||||
i = i + 1
|
||||
state = 'in_quote'
|
||||
else
|
||||
state = 'in_value'
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if #cur_arg > 0 then
|
||||
if state == 'in_arg' then
|
||||
pos_args[#pos_args + 1] = cur_arg
|
||||
elseif state == 'in_flag' then
|
||||
named_args[cur_arg] = true
|
||||
elseif state == 'in_value' then
|
||||
named_args[cur_arg] = cur_val
|
||||
elseif state == 'in_flag' then
|
||||
if ch:match('%s') then
|
||||
named_args[cur_arg] = true
|
||||
state = 'in_ws'
|
||||
else
|
||||
cur_arg = cur_arg .. ch
|
||||
end
|
||||
end
|
||||
elseif state == 'in_ws' then
|
||||
if is_char(ch) then
|
||||
if ch == '-' and peek(i) == '-' then
|
||||
state = 'in_flag'
|
||||
cur_arg = ''
|
||||
i = i + 1
|
||||
else
|
||||
state = 'in_arg'
|
||||
cur_arg = ch
|
||||
end
|
||||
end
|
||||
elseif state == 'in_value' then
|
||||
if is_char(ch) then
|
||||
cur_val = cur_val .. ch
|
||||
elseif ch:match('%s') then
|
||||
named_args[cur_arg] = cur_val
|
||||
cur_arg = ''
|
||||
state = 'in_ws'
|
||||
end
|
||||
elseif state == 'in_quote' then
|
||||
local next_ch = peek(i)
|
||||
if ch == '\\' and next_ch == cur_quote then
|
||||
cur_val = cur_val .. next_ch
|
||||
i = i + 1
|
||||
elseif ch == cur_quote then
|
||||
named_args[cur_arg] = cur_val
|
||||
state = 'in_ws'
|
||||
if next_ch ~= '' and not next_ch:match('%s') then
|
||||
error('malformed argument: ' .. next_ch)
|
||||
end
|
||||
else
|
||||
cur_val = cur_val .. ch
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return pos_args, named_args
|
||||
if #cur_arg > 0 then
|
||||
if state == 'in_arg' then
|
||||
pos_args[#pos_args + 1] = cur_arg
|
||||
elseif state == 'in_flag' then
|
||||
named_args[cur_arg] = true
|
||||
elseif state == 'in_value' then
|
||||
named_args[cur_arg] = cur_val
|
||||
end
|
||||
end
|
||||
|
||||
return pos_args, named_args
|
||||
end
|
||||
|
||||
return M
|
||||
|
907
lua/gitsigns/config.lua
generated
907
lua/gitsigns/config.lua
generated
File diff suppressed because it is too large
Load Diff
274
lua/gitsigns/current_line_blame.lua
generated
274
lua/gitsigns/current_line_blame.lua
generated
@ -19,196 +19,198 @@ local timer = uv.new_timer(true)
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
local wait_timer = wrap(vim.loop.timer_start, 4)
|
||||
|
||||
local function set_extmark(bufnr, row, opts)
|
||||
opts = opts or {}
|
||||
opts.id = 1
|
||||
api.nvim_buf_set_extmark(bufnr, namespace, row - 1, 0, opts)
|
||||
opts = opts or {}
|
||||
opts.id = 1
|
||||
api.nvim_buf_set_extmark(bufnr, namespace, row - 1, 0, opts)
|
||||
end
|
||||
|
||||
local function get_extmark(bufnr)
|
||||
local pos = api.nvim_buf_get_extmark_by_id(bufnr, namespace, 1, {})
|
||||
if pos[1] then
|
||||
return pos[1] + 1
|
||||
end
|
||||
return
|
||||
local pos = api.nvim_buf_get_extmark_by_id(bufnr, namespace, 1, {})
|
||||
if pos[1] then
|
||||
return pos[1] + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function reset(bufnr)
|
||||
bufnr = bufnr or current_buf()
|
||||
if not api.nvim_buf_is_valid(bufnr) then
|
||||
return
|
||||
end
|
||||
api.nvim_buf_del_extmark(bufnr, namespace, 1)
|
||||
vim.b[bufnr].gitsigns_blame_line_dict = nil
|
||||
bufnr = bufnr or current_buf()
|
||||
if not api.nvim_buf_is_valid(bufnr) then
|
||||
return
|
||||
end
|
||||
api.nvim_buf_del_extmark(bufnr, namespace, 1)
|
||||
vim.b[bufnr].gitsigns_blame_line_dict = nil
|
||||
end
|
||||
|
||||
-- TODO: expose as config
|
||||
local max_cache_size = 1000
|
||||
|
||||
local BlameCache = {Elem = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local BlameCache = { Elem = {} }
|
||||
|
||||
BlameCache.contents = {}
|
||||
|
||||
function BlameCache:add(bufnr, lnum, x)
|
||||
if not config._blame_cache then return end
|
||||
local scache = self.contents[bufnr]
|
||||
if scache.size <= max_cache_size then
|
||||
scache.cache[lnum] = x
|
||||
scache.size = scache.size + 1
|
||||
end
|
||||
if not config._blame_cache then
|
||||
return
|
||||
end
|
||||
local scache = self.contents[bufnr]
|
||||
if scache.size <= max_cache_size then
|
||||
scache.cache[lnum] = x
|
||||
scache.size = scache.size + 1
|
||||
end
|
||||
end
|
||||
|
||||
function BlameCache:get(bufnr, lnum)
|
||||
if not config._blame_cache then return end
|
||||
if not config._blame_cache then
|
||||
return
|
||||
end
|
||||
|
||||
-- init and invalidate
|
||||
local tick = vim.b[bufnr].changedtick
|
||||
if not self.contents[bufnr] or self.contents[bufnr].tick ~= tick then
|
||||
self.contents[bufnr] = { tick = tick, cache = {}, size = 0 }
|
||||
end
|
||||
-- init and invalidate
|
||||
local tick = vim.b[bufnr].changedtick
|
||||
if not self.contents[bufnr] or self.contents[bufnr].tick ~= tick then
|
||||
self.contents[bufnr] = { tick = tick, cache = {}, size = 0 }
|
||||
end
|
||||
|
||||
return self.contents[bufnr].cache[lnum]
|
||||
return self.contents[bufnr].cache[lnum]
|
||||
end
|
||||
|
||||
local function expand_blame_format(fmt, name, info)
|
||||
if info.author == name then
|
||||
info.author = 'You'
|
||||
end
|
||||
return util.expand_format(fmt, info, config.current_line_blame_formatter_opts.relative_time)
|
||||
if info.author == name then
|
||||
info.author = 'You'
|
||||
end
|
||||
return util.expand_format(fmt, info, config.current_line_blame_formatter_opts.relative_time)
|
||||
end
|
||||
|
||||
local function flatten_virt_text(virt_text)
|
||||
local res = {}
|
||||
for _, part in ipairs(virt_text) do
|
||||
res[#res + 1] = part[1]
|
||||
end
|
||||
return table.concat(res)
|
||||
local res = {}
|
||||
for _, part in ipairs(virt_text) do
|
||||
res[#res + 1] = part[1]
|
||||
end
|
||||
return table.concat(res)
|
||||
end
|
||||
|
||||
-- Update function, must be called in async context
|
||||
local update = void(function()
|
||||
local bufnr = current_buf()
|
||||
local lnum = api.nvim_win_get_cursor(0)[1]
|
||||
local bufnr = current_buf()
|
||||
local lnum = api.nvim_win_get_cursor(0)[1]
|
||||
|
||||
local old_lnum = get_extmark(bufnr)
|
||||
if old_lnum and lnum == old_lnum and BlameCache:get(bufnr, lnum) then
|
||||
-- Don't update if on the same line and we already have results
|
||||
return
|
||||
end
|
||||
local old_lnum = get_extmark(bufnr)
|
||||
if old_lnum and lnum == old_lnum and BlameCache:get(bufnr, lnum) then
|
||||
-- Don't update if on the same line and we already have results
|
||||
return
|
||||
end
|
||||
|
||||
if api.nvim_get_mode().mode == 'i' then
|
||||
reset(bufnr)
|
||||
return
|
||||
end
|
||||
if api.nvim_get_mode().mode == 'i' then
|
||||
reset(bufnr)
|
||||
return
|
||||
end
|
||||
|
||||
-- Set an empty extmark to save the line number.
|
||||
-- This will also clear virt_text.
|
||||
-- Only do this if there was already an extmark to avoid clearing the intro
|
||||
-- text.
|
||||
if get_extmark(bufnr) then
|
||||
reset(bufnr)
|
||||
set_extmark(bufnr, lnum)
|
||||
end
|
||||
-- Set an empty extmark to save the line number.
|
||||
-- This will also clear virt_text.
|
||||
-- Only do this if there was already an extmark to avoid clearing the intro
|
||||
-- text.
|
||||
if get_extmark(bufnr) then
|
||||
reset(bufnr)
|
||||
set_extmark(bufnr, lnum)
|
||||
end
|
||||
|
||||
-- Can't show extmarks on folded lines so skip
|
||||
if vim.fn.foldclosed(lnum) ~= -1 then
|
||||
return
|
||||
end
|
||||
-- Can't show extmarks on folded lines so skip
|
||||
if vim.fn.foldclosed(lnum) ~= -1 then
|
||||
return
|
||||
end
|
||||
|
||||
local opts = config.current_line_blame_opts
|
||||
local opts = config.current_line_blame_opts
|
||||
|
||||
-- Note because the same timer is re-used, this call has a debouncing effect.
|
||||
wait_timer(timer, opts.delay, 0)
|
||||
scheduler()
|
||||
-- Note because the same timer is re-used, this call has a debouncing effect.
|
||||
wait_timer(timer, opts.delay, 0)
|
||||
scheduler()
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache or not bcache.git_obj.object_name then
|
||||
return
|
||||
end
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache or not bcache.git_obj.object_name then
|
||||
return
|
||||
end
|
||||
|
||||
local result = BlameCache:get(bufnr, lnum)
|
||||
if not result then
|
||||
local buftext = util.buf_lines(bufnr)
|
||||
result = bcache.git_obj:run_blame(buftext, lnum, opts.ignore_whitespace)
|
||||
BlameCache:add(bufnr, lnum, result)
|
||||
scheduler()
|
||||
end
|
||||
local result = BlameCache:get(bufnr, lnum)
|
||||
if not result then
|
||||
local buftext = util.buf_lines(bufnr)
|
||||
result = bcache.git_obj:run_blame(buftext, lnum, opts.ignore_whitespace)
|
||||
BlameCache:add(bufnr, lnum, result)
|
||||
scheduler()
|
||||
end
|
||||
|
||||
local lnum1 = api.nvim_win_get_cursor(0)[1]
|
||||
if bufnr == current_buf() and lnum ~= lnum1 then
|
||||
-- Cursor has moved during events; abort
|
||||
return
|
||||
end
|
||||
local lnum1 = api.nvim_win_get_cursor(0)[1]
|
||||
if bufnr == current_buf() and lnum ~= lnum1 then
|
||||
-- Cursor has moved during events; abort
|
||||
return
|
||||
end
|
||||
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
-- Buffer is no longer loaded; abort
|
||||
return
|
||||
end
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
-- Buffer is no longer loaded; abort
|
||||
return
|
||||
end
|
||||
|
||||
vim.b[bufnr].gitsigns_blame_line_dict = result
|
||||
vim.b[bufnr].gitsigns_blame_line_dict = result
|
||||
|
||||
if result then
|
||||
local virt_text
|
||||
local clb_formatter = result.author == 'Not Committed Yet' and
|
||||
config.current_line_blame_formatter_nc or
|
||||
config.current_line_blame_formatter
|
||||
if type(clb_formatter) == "string" then
|
||||
virt_text = { {
|
||||
expand_blame_format(clb_formatter, bcache.git_obj.repo.username, result),
|
||||
'GitSignsCurrentLineBlame',
|
||||
}, }
|
||||
else -- function
|
||||
virt_text = clb_formatter(
|
||||
bcache.git_obj.repo.username,
|
||||
result,
|
||||
config.current_line_blame_formatter_opts)
|
||||
if result then
|
||||
local virt_text
|
||||
local clb_formatter = result.author == 'Not Committed Yet'
|
||||
and config.current_line_blame_formatter_nc
|
||||
or config.current_line_blame_formatter
|
||||
if type(clb_formatter) == 'string' then
|
||||
virt_text = {
|
||||
{
|
||||
expand_blame_format(clb_formatter, bcache.git_obj.repo.username, result),
|
||||
'GitSignsCurrentLineBlame',
|
||||
},
|
||||
}
|
||||
else -- function
|
||||
virt_text = clb_formatter(
|
||||
bcache.git_obj.repo.username,
|
||||
result,
|
||||
config.current_line_blame_formatter_opts
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
vim.b[bufnr].gitsigns_blame_line = flatten_virt_text(virt_text)
|
||||
|
||||
vim.b[bufnr].gitsigns_blame_line = flatten_virt_text(virt_text)
|
||||
|
||||
if opts.virt_text then
|
||||
set_extmark(bufnr, lnum, {
|
||||
virt_text = virt_text,
|
||||
virt_text_pos = opts.virt_text_pos,
|
||||
priority = opts.virt_text_priority,
|
||||
hl_mode = 'combine',
|
||||
})
|
||||
end
|
||||
end
|
||||
if opts.virt_text then
|
||||
set_extmark(bufnr, lnum, {
|
||||
virt_text = virt_text,
|
||||
virt_text_pos = opts.virt_text_pos,
|
||||
priority = opts.virt_text_priority,
|
||||
hl_mode = 'combine',
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
M.setup = function()
|
||||
local group = api.nvim_create_augroup('gitsigns_blame', {})
|
||||
local group = api.nvim_create_augroup('gitsigns_blame', {})
|
||||
|
||||
for k, _ in pairs(cache) do
|
||||
reset(k)
|
||||
end
|
||||
for k, _ in pairs(cache) do
|
||||
reset(k)
|
||||
end
|
||||
|
||||
if config.current_line_blame then
|
||||
api.nvim_create_autocmd({ 'FocusGained', 'BufEnter', 'CursorMoved', 'CursorMovedI' }, {
|
||||
group = group, callback = function() update() end,
|
||||
})
|
||||
if config.current_line_blame then
|
||||
api.nvim_create_autocmd({ 'FocusGained', 'BufEnter', 'CursorMoved', 'CursorMovedI' }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
update()
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd({ 'InsertEnter', 'FocusLost', 'BufLeave' }, {
|
||||
group = group, callback = function() reset() end,
|
||||
})
|
||||
api.nvim_create_autocmd({ 'InsertEnter', 'FocusLost', 'BufLeave' }, {
|
||||
group = group,
|
||||
callback = function()
|
||||
reset()
|
||||
end,
|
||||
})
|
||||
|
||||
-- Call via vim.schedule to avoid the debounce timer killing the async
|
||||
-- coroutine
|
||||
vim.schedule(update)
|
||||
end
|
||||
-- Call via vim.schedule to avoid the debounce timer killing the async
|
||||
-- coroutine
|
||||
vim.schedule(update)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
84
lua/gitsigns/debounce.lua
generated
84
lua/gitsigns/debounce.lua
generated
@ -2,9 +2,6 @@ local uv = require('gitsigns.uv')
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Debounces a function on the trailing edge.
|
||||
---
|
||||
--- @generic F: function
|
||||
@ -12,17 +9,16 @@ local M = {}
|
||||
--- @param fn F Function to debounce
|
||||
--- @return F Debounced function.
|
||||
function M.debounce_trailing(ms, fn)
|
||||
local timer = uv.new_timer(true)
|
||||
return function(...)
|
||||
local argv = { ... }
|
||||
timer:start(ms, 0, function()
|
||||
timer:stop()
|
||||
fn(unpack(argv))
|
||||
end)
|
||||
end
|
||||
local timer = uv.new_timer(true)
|
||||
return function(...)
|
||||
local argv = { ... }
|
||||
timer:start(ms, 0, function()
|
||||
timer:stop()
|
||||
fn(unpack(argv))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Throttles a function on the leading edge.
|
||||
---
|
||||
--- @generic F: function
|
||||
@ -30,18 +26,18 @@ end
|
||||
--- @param fn F Function to throttle
|
||||
--- @return F throttled function.
|
||||
function M.throttle_leading(ms, fn)
|
||||
local timer = uv.new_timer(true)
|
||||
local running = false
|
||||
return function(...)
|
||||
if not running then
|
||||
timer:start(ms, 0, function()
|
||||
running = false
|
||||
timer:stop()
|
||||
end)
|
||||
running = true
|
||||
fn(...)
|
||||
end
|
||||
end
|
||||
local timer = uv.new_timer(true)
|
||||
local running = false
|
||||
return function(...)
|
||||
if not running then
|
||||
timer:start(ms, 0, function()
|
||||
running = false
|
||||
timer:stop()
|
||||
end)
|
||||
running = true
|
||||
fn(...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Throttles a function using the first argument as an ID
|
||||
@ -61,26 +57,26 @@ end
|
||||
--- @param schedule boolean
|
||||
--- @return F throttled function.
|
||||
function M.throttle_by_id(fn, schedule)
|
||||
local scheduled = {} --- @type table<any,boolean>
|
||||
local running = {} --- @type table<any,boolean>
|
||||
return function(id, ...)
|
||||
if scheduled[id] then
|
||||
-- If fn is already scheduled, then drop
|
||||
return
|
||||
end
|
||||
if not running[id] or schedule then
|
||||
scheduled[id] = true
|
||||
end
|
||||
if running[id] then
|
||||
return
|
||||
end
|
||||
while scheduled[id] do
|
||||
scheduled[id] = nil
|
||||
running[id] = true
|
||||
fn(id, ...)
|
||||
running[id] = nil
|
||||
end
|
||||
end
|
||||
local scheduled = {} --- @type table<any,boolean>
|
||||
local running = {} --- @type table<any,boolean>
|
||||
return function(id, ...)
|
||||
if scheduled[id] then
|
||||
-- If fn is already scheduled, then drop
|
||||
return
|
||||
end
|
||||
if not running[id] or schedule then
|
||||
scheduled[id] = true
|
||||
end
|
||||
if running[id] then
|
||||
return
|
||||
end
|
||||
while scheduled[id] do
|
||||
scheduled[id] = nil
|
||||
running[id] = true
|
||||
fn(id, ...)
|
||||
running[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
54
lua/gitsigns/debug.lua
generated
54
lua/gitsigns/debug.lua
generated
@ -6,45 +6,45 @@ local M = {}
|
||||
--- @param path string[]
|
||||
--- @return any
|
||||
local function process(raw_item, path)
|
||||
if path[#path] == vim.inspect.METATABLE then
|
||||
return nil
|
||||
elseif type(raw_item) == "function" then
|
||||
return nil
|
||||
elseif type(raw_item) == "table" then
|
||||
local key = path[#path]
|
||||
if key == 'compare_text' or key == 'compare_text_head' then
|
||||
local item = raw_item
|
||||
return { '...', length = #item, head = item[1] }
|
||||
elseif not vim.tbl_isempty(raw_item) and key == 'staged_diffs' then
|
||||
return { '...', length = #vim.tbl_keys(raw_item) }
|
||||
end
|
||||
end
|
||||
return raw_item
|
||||
if path[#path] == vim.inspect.METATABLE then
|
||||
return nil
|
||||
elseif type(raw_item) == 'function' then
|
||||
return nil
|
||||
elseif type(raw_item) == 'table' then
|
||||
local key = path[#path]
|
||||
if key == 'compare_text' or key == 'compare_text_head' then
|
||||
local item = raw_item
|
||||
return { '...', length = #item, head = item[1] }
|
||||
elseif not vim.tbl_isempty(raw_item) and key == 'staged_diffs' then
|
||||
return { '...', length = #vim.tbl_keys(raw_item) }
|
||||
end
|
||||
end
|
||||
return raw_item
|
||||
end
|
||||
|
||||
--- @return any
|
||||
function M.dump_cache()
|
||||
-- TODO(lewis6991): hack: use package.loaded to avoid circular deps
|
||||
local cache = (require('gitsigns.cache')).cache
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({ { text } }, false, {})
|
||||
return cache
|
||||
-- TODO(lewis6991): hack: use package.loaded to avoid circular deps
|
||||
local cache = (require('gitsigns.cache')).cache
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({ { text } }, false, {})
|
||||
return cache
|
||||
end
|
||||
|
||||
--- @param noecho boolean
|
||||
--- @return string[]
|
||||
function M.debug_messages(noecho)
|
||||
if noecho then
|
||||
return log.messages
|
||||
else
|
||||
for _, m in ipairs(log.messages) do
|
||||
vim.api.nvim_echo({ { m } }, false, {})
|
||||
end
|
||||
end
|
||||
if noecho then
|
||||
return log.messages
|
||||
else
|
||||
for _, m in ipairs(log.messages) do
|
||||
vim.api.nvim_echo({ { m } }, false, {})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.clear_debug()
|
||||
log.messages = {}
|
||||
log.messages = {}
|
||||
end
|
||||
|
||||
return M
|
||||
|
154
lua/gitsigns/debug/log.lua
generated
154
lua/gitsigns/debug/log.lua
generated
@ -1,109 +1,125 @@
|
||||
local M = {
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages = {},
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages = {},
|
||||
}
|
||||
|
||||
local function getvarvalue(name, lvl)
|
||||
lvl = lvl + 1
|
||||
local value
|
||||
local found
|
||||
lvl = lvl + 1
|
||||
local value
|
||||
local found
|
||||
|
||||
-- try local variables
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getlocal(lvl, i)
|
||||
if not n then break end
|
||||
if n == name then
|
||||
value = v
|
||||
found = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if found then return value end
|
||||
-- try local variables
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getlocal(lvl, i)
|
||||
if not n then
|
||||
break
|
||||
end
|
||||
if n == name then
|
||||
value = v
|
||||
found = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if found then
|
||||
return value
|
||||
end
|
||||
|
||||
-- try upvalues
|
||||
local func = debug.getinfo(lvl).func
|
||||
i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i)
|
||||
if not n then break end
|
||||
if n == name then return v end
|
||||
i = i + 1
|
||||
end
|
||||
-- try upvalues
|
||||
local func = debug.getinfo(lvl).func
|
||||
i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i)
|
||||
if not n then
|
||||
break
|
||||
end
|
||||
if n == name then
|
||||
return v
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- not found; get global
|
||||
return getfenv(func)[name]
|
||||
-- not found; get global
|
||||
return getfenv(func)[name]
|
||||
end
|
||||
|
||||
local function get_context(lvl)
|
||||
lvl = lvl + 1
|
||||
local ret = {}
|
||||
ret.name = getvarvalue('__FUNC__', lvl)
|
||||
if not ret.name then
|
||||
local name0 = debug.getinfo(lvl, 'n').name or ''
|
||||
ret.name = name0:gsub('(.*)%d+$', '%1')
|
||||
end
|
||||
ret.bufnr = getvarvalue('bufnr', lvl) or
|
||||
getvarvalue('_bufnr', lvl) or
|
||||
getvarvalue('cbuf', lvl) or
|
||||
getvarvalue('buf', lvl)
|
||||
lvl = lvl + 1
|
||||
local ret = {}
|
||||
ret.name = getvarvalue('__FUNC__', lvl)
|
||||
if not ret.name then
|
||||
local name0 = debug.getinfo(lvl, 'n').name or ''
|
||||
ret.name = name0:gsub('(.*)%d+$', '%1')
|
||||
end
|
||||
ret.bufnr = getvarvalue('bufnr', lvl)
|
||||
or getvarvalue('_bufnr', lvl)
|
||||
or getvarvalue('cbuf', lvl)
|
||||
or getvarvalue('buf', lvl)
|
||||
|
||||
return ret
|
||||
return ret
|
||||
end
|
||||
|
||||
-- If called in a callback then make sure the callback defines a __FUNC__
|
||||
-- variable which can be used to identify the name of the function.
|
||||
local function cprint(obj, lvl)
|
||||
lvl = lvl + 1
|
||||
local msg = type(obj) == "string" and obj or vim.inspect(obj)
|
||||
local ctx = get_context(lvl)
|
||||
local msg2
|
||||
if ctx.bufnr then
|
||||
msg2 = string.format('%s(%s): %s', ctx.name, ctx.bufnr, msg)
|
||||
else
|
||||
msg2 = string.format('%s: %s', ctx.name, msg)
|
||||
end
|
||||
table.insert(M.messages, msg2)
|
||||
lvl = lvl + 1
|
||||
local msg = type(obj) == 'string' and obj or vim.inspect(obj)
|
||||
local ctx = get_context(lvl)
|
||||
local msg2
|
||||
if ctx.bufnr then
|
||||
msg2 = string.format('%s(%s): %s', ctx.name, ctx.bufnr, msg)
|
||||
else
|
||||
msg2 = string.format('%s: %s', ctx.name, msg)
|
||||
end
|
||||
table.insert(M.messages, msg2)
|
||||
end
|
||||
|
||||
function M.dprint(obj)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj, 2)
|
||||
if not M.debug_mode then
|
||||
return
|
||||
end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.dprintf(obj, ...)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj:format(...), 2)
|
||||
if not M.debug_mode then
|
||||
return
|
||||
end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
function M.vprint(obj)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj, 2)
|
||||
if not (M.debug_mode and M.verbose) then
|
||||
return
|
||||
end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.vprintf(obj, ...)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj:format(...), 2)
|
||||
if not (M.debug_mode and M.verbose) then
|
||||
return
|
||||
end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
local function eprint(msg, level)
|
||||
local info = debug.getinfo(level + 2, 'Sl')
|
||||
if info then
|
||||
msg = string.format('(ERROR) %s(%d): %s', info.short_src, info.currentline, msg)
|
||||
end
|
||||
M.messages[#M.messages + 1] = msg
|
||||
if M.debug_mode then
|
||||
error(msg)
|
||||
end
|
||||
local info = debug.getinfo(level + 2, 'Sl')
|
||||
if info then
|
||||
msg = string.format('(ERROR) %s(%d): %s', info.short_src, info.currentline, msg)
|
||||
end
|
||||
M.messages[#M.messages + 1] = msg
|
||||
if M.debug_mode then
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
|
||||
function M.eprint(msg)
|
||||
eprint(msg, 1)
|
||||
eprint(msg, 1)
|
||||
end
|
||||
|
||||
function M.eprintf(fmt, ...)
|
||||
eprint(fmt:format(...), 1)
|
||||
eprint(fmt:format(...), 1)
|
||||
end
|
||||
|
||||
return M
|
||||
|
25
lua/gitsigns/diff.lua
generated
25
lua/gitsigns/diff.lua
generated
@ -1,18 +1,17 @@
|
||||
local config = require('gitsigns.config').config
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
|
||||
return function(a, b, linematch)
|
||||
local diff_opts = config.diff_opts
|
||||
local f
|
||||
if diff_opts.internal then
|
||||
f = require('gitsigns.diff_int').run_diff
|
||||
else
|
||||
f = require('gitsigns.diff_ext').run_diff
|
||||
end
|
||||
local diff_opts = config.diff_opts
|
||||
local f
|
||||
if diff_opts.internal then
|
||||
f = require('gitsigns.diff_int').run_diff
|
||||
else
|
||||
f = require('gitsigns.diff_ext').run_diff
|
||||
end
|
||||
|
||||
local linematch0
|
||||
if linematch ~= false then
|
||||
linematch0 = diff_opts.linematch
|
||||
end
|
||||
return f(a, b, diff_opts.algorithm, diff_opts.indent_heuristic, linematch0)
|
||||
local linematch0 --- @type boolean?
|
||||
if linematch ~= false then
|
||||
linematch0 = diff_opts.linematch
|
||||
end
|
||||
return f(a, b, diff_opts.algorithm, diff_opts.indent_heuristic, linematch0)
|
||||
end
|
||||
|
109
lua/gitsigns/diff_ext.lua
generated
109
lua/gitsigns/diff_ext.lua
generated
@ -1,80 +1,73 @@
|
||||
local git_diff = require('gitsigns.git').diff
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
local gs_hunks = require('gitsigns.hunks')
|
||||
local Hunk = gs_hunks.Hunk
|
||||
local util = require('gitsigns.util')
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local M = {}
|
||||
-- Async function
|
||||
|
||||
|
||||
-- Async function
|
||||
|
||||
local function write_to_file(path, text)
|
||||
local f, err = io.open(path, 'wb')
|
||||
if f == nil then
|
||||
error(err)
|
||||
end
|
||||
for _, l in ipairs(text) do
|
||||
f:write(l)
|
||||
f:write('\n')
|
||||
end
|
||||
f:close()
|
||||
local f, err = io.open(path, 'wb')
|
||||
if f == nil then
|
||||
error(err)
|
||||
end
|
||||
for _, l in ipairs(text) do
|
||||
f:write(l)
|
||||
f:write('\n')
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
|
||||
M.run_diff = function(
|
||||
text_cmp,
|
||||
text_buf,
|
||||
diff_algo,
|
||||
indent_heuristic)
|
||||
M.run_diff = function(text_cmp, text_buf, diff_algo, indent_heuristic)
|
||||
local results = {}
|
||||
|
||||
local results = {}
|
||||
-- tmpname must not be called in a callback
|
||||
if vim.in_fast_event() then
|
||||
scheduler()
|
||||
end
|
||||
|
||||
-- tmpname must not be called in a callback
|
||||
if vim.in_fast_event() then
|
||||
scheduler()
|
||||
end
|
||||
local file_buf = util.tmpname()
|
||||
local file_cmp = util.tmpname()
|
||||
|
||||
local file_buf = util.tmpname()
|
||||
local file_cmp = util.tmpname()
|
||||
write_to_file(file_buf, text_buf)
|
||||
write_to_file(file_cmp, text_cmp)
|
||||
|
||||
write_to_file(file_buf, text_buf)
|
||||
write_to_file(file_cmp, text_cmp)
|
||||
-- Taken from gitgutter, diff.vim:
|
||||
--
|
||||
-- If a file has CRLF line endings and git's core.autocrlf is true, the file
|
||||
-- in git's object store will have LF line endings. Writing it out via
|
||||
-- git-show will produce a file with LF line endings.
|
||||
--
|
||||
-- If this last file is one of the files passed to git-diff, git-diff will
|
||||
-- convert its line endings to CRLF before diffing -- which is what we want
|
||||
-- but also by default outputs a warning on stderr.
|
||||
--
|
||||
-- warning: LF will be replace by CRLF in <temp file>.
|
||||
-- The file will have its original line endings in your working directory.
|
||||
--
|
||||
-- We can safely ignore the warning, we turn it off by passing the '-c
|
||||
-- "core.safecrlf=false"' argument to git-diff.
|
||||
|
||||
-- Taken from gitgutter, diff.vim:
|
||||
--
|
||||
-- If a file has CRLF line endings and git's core.autocrlf is true, the file
|
||||
-- in git's object store will have LF line endings. Writing it out via
|
||||
-- git-show will produce a file with LF line endings.
|
||||
--
|
||||
-- If this last file is one of the files passed to git-diff, git-diff will
|
||||
-- convert its line endings to CRLF before diffing -- which is what we want
|
||||
-- but also by default outputs a warning on stderr.
|
||||
--
|
||||
-- warning: LF will be replace by CRLF in <temp file>.
|
||||
-- The file will have its original line endings in your working directory.
|
||||
--
|
||||
-- We can safely ignore the warning, we turn it off by passing the '-c
|
||||
-- "core.safecrlf=false"' argument to git-diff.
|
||||
local out = git_diff(file_cmp, file_buf, indent_heuristic, diff_algo)
|
||||
|
||||
local out = git_diff(file_cmp, file_buf, indent_heuristic, diff_algo)
|
||||
|
||||
for _, line in ipairs(out) do
|
||||
if vim.startswith(line, '@@') then
|
||||
results[#results + 1] = gs_hunks.parse_diff_line(line)
|
||||
elseif #results > 0 then
|
||||
local r = results[#results]
|
||||
if line:sub(1, 1) == '-' then
|
||||
r.removed.lines[#r.removed.lines + 1] = line:sub(2)
|
||||
elseif line:sub(1, 1) == '+' then
|
||||
r.added.lines[#r.added.lines + 1] = line:sub(2)
|
||||
end
|
||||
for _, line in ipairs(out) do
|
||||
if vim.startswith(line, '@@') then
|
||||
results[#results + 1] = gs_hunks.parse_diff_line(line)
|
||||
elseif #results > 0 then
|
||||
local r = results[#results]
|
||||
if line:sub(1, 1) == '-' then
|
||||
r.removed.lines[#r.removed.lines + 1] = line:sub(2)
|
||||
elseif line:sub(1, 1) == '+' then
|
||||
r.added.lines[#r.added.lines + 1] = line:sub(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
os.remove(file_buf)
|
||||
os.remove(file_cmp)
|
||||
return results
|
||||
os.remove(file_buf)
|
||||
os.remove(file_cmp)
|
||||
return results
|
||||
end
|
||||
|
||||
return M
|
||||
|
208
lua/gitsigns/diff_int.lua
generated
208
lua/gitsigns/diff_int.lua
generated
@ -1,149 +1,133 @@
|
||||
local create_hunk = require("gitsigns.hunks").create_hunk
|
||||
local create_hunk = require('gitsigns.hunks').create_hunk
|
||||
local Hunk = require('gitsigns.hunks').Hunk
|
||||
local config = require('gitsigns.config').config
|
||||
local async = require('gitsigns.async')
|
||||
|
||||
local M = {}
|
||||
|
||||
local run_diff_xdl = function(fa, fb, algorithm, indent_heuristic, linematch)
|
||||
local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n'
|
||||
local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local run_diff_xdl = function(
|
||||
fa, fb,
|
||||
algorithm, indent_heuristic,
|
||||
linematch)
|
||||
|
||||
|
||||
local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n'
|
||||
local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n'
|
||||
|
||||
return vim.diff(a, b, {
|
||||
result_type = 'indices',
|
||||
algorithm = algorithm,
|
||||
indent_heuristic = indent_heuristic,
|
||||
linematch = linematch,
|
||||
})
|
||||
return vim.diff(a, b, {
|
||||
result_type = 'indices',
|
||||
algorithm = algorithm,
|
||||
indent_heuristic = indent_heuristic,
|
||||
linematch = linematch,
|
||||
})
|
||||
end
|
||||
|
||||
local run_diff_xdl_async = async.wrap(function(
|
||||
fa, fb,
|
||||
algorithm, indent_heuristic,
|
||||
linematch,
|
||||
callback)
|
||||
local run_diff_xdl_async = async.wrap(
|
||||
function(fa, fb, algorithm, indent_heuristic, linematch, callback)
|
||||
local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n'
|
||||
local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n'
|
||||
|
||||
vim.loop
|
||||
.new_work(function(a0, b0, algorithm0, indent_heuristic0, linematch0)
|
||||
return vim.mpack.encode(vim.diff(a0, b0, {
|
||||
result_type = 'indices',
|
||||
algorithm = algorithm0,
|
||||
indent_heuristic = indent_heuristic0,
|
||||
linematch = linematch0,
|
||||
}))
|
||||
end, function(r)
|
||||
callback(vim.mpack.decode(r))
|
||||
end)
|
||||
:queue(a, b, algorithm, indent_heuristic, linematch)
|
||||
end,
|
||||
6
|
||||
)
|
||||
|
||||
local a = vim.tbl_isempty(fa) and '' or table.concat(fa, '\n') .. '\n'
|
||||
local b = vim.tbl_isempty(fb) and '' or table.concat(fb, '\n') .. '\n'
|
||||
M.run_diff = async.void(function(fa, fb, diff_algo, indent_heuristic, linematch)
|
||||
local run_diff0
|
||||
if config._threaded_diff and vim.is_thread then
|
||||
run_diff0 = run_diff_xdl_async
|
||||
else
|
||||
run_diff0 = run_diff_xdl
|
||||
end
|
||||
|
||||
vim.loop.new_work(function(
|
||||
a0, b0,
|
||||
algorithm0, indent_heuristic0,
|
||||
linematch0)
|
||||
local results = run_diff0(fa, fb, diff_algo, indent_heuristic, linematch)
|
||||
|
||||
return vim.mpack.encode(vim.diff(a0, b0, {
|
||||
result_type = 'indices',
|
||||
algorithm = algorithm0,
|
||||
indent_heuristic = indent_heuristic0,
|
||||
linematch = linematch0,
|
||||
}))
|
||||
end, function(r)
|
||||
callback(vim.mpack.decode(r))
|
||||
end):queue(a, b, algorithm, indent_heuristic, linematch)
|
||||
end, 6)
|
||||
local hunks = {}
|
||||
|
||||
M.run_diff = async.void(function(
|
||||
fa, fb,
|
||||
diff_algo, indent_heuristic,
|
||||
linematch)
|
||||
|
||||
local run_diff0
|
||||
if config._threaded_diff and vim.is_thread then
|
||||
run_diff0 = run_diff_xdl_async
|
||||
else
|
||||
run_diff0 = run_diff_xdl
|
||||
end
|
||||
|
||||
local results = run_diff0(fa, fb, diff_algo, indent_heuristic, linematch)
|
||||
|
||||
local hunks = {}
|
||||
|
||||
for _, r in ipairs(results) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
local hunk = create_hunk(rs, rc, as, ac)
|
||||
if rc > 0 then
|
||||
for i = rs, rs + rc - 1 do
|
||||
hunk.removed.lines[#hunk.removed.lines + 1] = fa[i] or ''
|
||||
end
|
||||
for _, r in ipairs(results) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
local hunk = create_hunk(rs, rc, as, ac)
|
||||
if rc > 0 then
|
||||
for i = rs, rs + rc - 1 do
|
||||
hunk.removed.lines[#hunk.removed.lines + 1] = fa[i] or ''
|
||||
end
|
||||
if ac > 0 then
|
||||
for i = as, as + ac - 1 do
|
||||
hunk.added.lines[#hunk.added.lines + 1] = fb[i] or ''
|
||||
end
|
||||
end
|
||||
if ac > 0 then
|
||||
for i = as, as + ac - 1 do
|
||||
hunk.added.lines[#hunk.added.lines + 1] = fb[i] or ''
|
||||
end
|
||||
hunks[#hunks + 1] = hunk
|
||||
end
|
||||
end
|
||||
hunks[#hunks + 1] = hunk
|
||||
end
|
||||
|
||||
return hunks
|
||||
return hunks
|
||||
end)
|
||||
|
||||
|
||||
|
||||
local gaps_between_regions = 5
|
||||
|
||||
local function denoise_hunks(hunks)
|
||||
-- Denoise the hunks
|
||||
local ret = { hunks[1] }
|
||||
for j = 2, #hunks do
|
||||
local h, n = ret[#ret], hunks[j]
|
||||
if not h or not n then break end
|
||||
if n.added.start - h.added.start - h.added.count < gaps_between_regions then
|
||||
h.added.count = n.added.start + n.added.count - h.added.start
|
||||
h.removed.count = n.removed.start + n.removed.count - h.removed.start
|
||||
-- Denoise the hunks
|
||||
local ret = { hunks[1] }
|
||||
for j = 2, #hunks do
|
||||
local h, n = ret[#ret], hunks[j]
|
||||
if not h or not n then
|
||||
break
|
||||
end
|
||||
if n.added.start - h.added.start - h.added.count < gaps_between_regions then
|
||||
h.added.count = n.added.start + n.added.count - h.added.start
|
||||
h.removed.count = n.removed.start + n.removed.count - h.removed.start
|
||||
|
||||
if h.added.count > 0 or h.removed.count > 0 then
|
||||
h.type = 'change'
|
||||
end
|
||||
else
|
||||
ret[#ret + 1] = n
|
||||
if h.added.count > 0 or h.removed.count > 0 then
|
||||
h.type = 'change'
|
||||
end
|
||||
end
|
||||
return ret
|
||||
else
|
||||
ret[#ret + 1] = n
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function M.run_word_diff(removed, added)
|
||||
local adds = {}
|
||||
local rems = {}
|
||||
local adds = {}
|
||||
local rems = {}
|
||||
|
||||
if #removed ~= #added then
|
||||
return rems, adds
|
||||
end
|
||||
if #removed ~= #added then
|
||||
return rems, adds
|
||||
end
|
||||
|
||||
for i = 1, #removed do
|
||||
-- pair lines by position
|
||||
local a, b = vim.split(removed[i], ''), vim.split(added[i], '')
|
||||
for i = 1, #removed do
|
||||
-- pair lines by position
|
||||
local a, b = vim.split(removed[i], ''), vim.split(added[i], '')
|
||||
|
||||
local hunks = {}
|
||||
for _, r in ipairs(run_diff_xdl(a, b)) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
local hunks = {}
|
||||
for _, r in ipairs(run_diff_xdl(a, b)) do
|
||||
local rs, rc, as, ac = unpack(r)
|
||||
|
||||
-- Balance of the unknown offset done in hunk_func
|
||||
if rc == 0 then rs = rs + 1 end
|
||||
if ac == 0 then as = as + 1 end
|
||||
|
||||
hunks[#hunks + 1] = create_hunk(rs, rc, as, ac)
|
||||
-- Balance of the unknown offset done in hunk_func
|
||||
if rc == 0 then
|
||||
rs = rs + 1
|
||||
end
|
||||
if ac == 0 then
|
||||
as = as + 1
|
||||
end
|
||||
|
||||
hunks = denoise_hunks(hunks)
|
||||
hunks[#hunks + 1] = create_hunk(rs, rc, as, ac)
|
||||
end
|
||||
|
||||
for _, h in ipairs(hunks) do
|
||||
adds[#adds + 1] = { i, h.type, h.added.start, h.added.start + h.added.count }
|
||||
rems[#rems + 1] = { i, h.type, h.removed.start, h.removed.start + h.removed.count }
|
||||
end
|
||||
end
|
||||
return rems, adds
|
||||
hunks = denoise_hunks(hunks)
|
||||
|
||||
for _, h in ipairs(hunks) do
|
||||
adds[#adds + 1] = { i, h.type, h.added.start, h.added.start + h.added.count }
|
||||
rems[#rems + 1] = { i, h.type, h.removed.start, h.removed.start + h.removed.count }
|
||||
end
|
||||
end
|
||||
return rems, adds
|
||||
end
|
||||
|
||||
return M
|
||||
|
279
lua/gitsigns/diffthis.lua
generated
279
lua/gitsigns/diffthis.lua
generated
@ -16,186 +16,179 @@ local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local input = awrap(vim.ui.input, 2)
|
||||
|
||||
local M = {DiffthisOpts = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = { DiffthisOpts = {} }
|
||||
|
||||
local bufread = void(function(bufnr, dbufnr, base, bcache)
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local text
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
text = bcache.compare_text
|
||||
else
|
||||
local err
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if err then
|
||||
error(err, 2)
|
||||
end
|
||||
scheduler()
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
text = util.strip_cr(text)
|
||||
end
|
||||
end
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local text
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
text = bcache.compare_text
|
||||
else
|
||||
local err
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if err then
|
||||
error(err, 2)
|
||||
end
|
||||
scheduler()
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
text = util.strip_cr(text)
|
||||
end
|
||||
end
|
||||
|
||||
local modifiable = vim.bo[dbufnr].modifiable
|
||||
vim.bo[dbufnr].modifiable = true
|
||||
util.set_lines(dbufnr, 0, -1, text)
|
||||
local modifiable = vim.bo[dbufnr].modifiable
|
||||
vim.bo[dbufnr].modifiable = true
|
||||
util.set_lines(dbufnr, 0, -1, text)
|
||||
|
||||
vim.bo[dbufnr].modifiable = modifiable
|
||||
vim.bo[dbufnr].modified = false
|
||||
vim.bo[dbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[dbufnr].bufhidden = 'wipe'
|
||||
vim.bo[dbufnr].modifiable = modifiable
|
||||
vim.bo[dbufnr].modified = false
|
||||
vim.bo[dbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[dbufnr].bufhidden = 'wipe'
|
||||
end)
|
||||
|
||||
local bufwrite = void(function(bufnr, dbufnr, base, bcache)
|
||||
local buftext = util.buf_lines(dbufnr)
|
||||
bcache.git_obj:stage_lines(buftext)
|
||||
scheduler()
|
||||
vim.bo[dbufnr].modified = false
|
||||
-- If diff buffer base matches the bcache base then also update the
|
||||
-- signs.
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
bcache.compare_text = buftext
|
||||
manager.update(bufnr, bcache)
|
||||
end
|
||||
local buftext = util.buf_lines(dbufnr)
|
||||
bcache.git_obj:stage_lines(buftext)
|
||||
scheduler()
|
||||
vim.bo[dbufnr].modified = false
|
||||
-- If diff buffer base matches the bcache base then also update the
|
||||
-- signs.
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
bcache.compare_text = buftext
|
||||
manager.update(bufnr, bcache)
|
||||
end
|
||||
end)
|
||||
|
||||
local function run(base, diffthis, opts)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
opts = opts or {}
|
||||
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local bufname = bcache:get_rev_bufname(comp_rev)
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local bufname = bcache:get_rev_bufname(comp_rev)
|
||||
|
||||
local dbuf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(dbuf, bufname)
|
||||
local dbuf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(dbuf, bufname)
|
||||
|
||||
local ok, err = pcall(bufread, bufnr, dbuf, base, bcache)
|
||||
if not ok then
|
||||
message.error(err)
|
||||
scheduler()
|
||||
vim.cmd('bdelete')
|
||||
if diffthis then
|
||||
vim.cmd('diffoff')
|
||||
end
|
||||
return
|
||||
end
|
||||
local ok, err = pcall(bufread, bufnr, dbuf, base, bcache)
|
||||
if not ok then
|
||||
message.error(err)
|
||||
scheduler()
|
||||
vim.cmd('bdelete')
|
||||
if diffthis then
|
||||
vim.cmd('diffoff')
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if comp_rev == ':0' then
|
||||
vim.bo[dbuf].buftype = 'acwrite'
|
||||
if comp_rev == ':0' then
|
||||
vim.bo[dbuf].buftype = 'acwrite'
|
||||
|
||||
api.nvim_create_autocmd('BufReadCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
if diffthis then
|
||||
vim.cmd('diffthis')
|
||||
end
|
||||
end,
|
||||
})
|
||||
api.nvim_create_autocmd('BufReadCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
if diffthis then
|
||||
vim.cmd('diffthis')
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('BufWriteCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufwrite(bufnr, dbuf, base, bcache)
|
||||
end,
|
||||
})
|
||||
else
|
||||
vim.bo[dbuf].buftype = 'nowrite'
|
||||
vim.bo[dbuf].modifiable = false
|
||||
end
|
||||
api.nvim_create_autocmd('BufWriteCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufwrite(bufnr, dbuf, base, bcache)
|
||||
end,
|
||||
})
|
||||
else
|
||||
vim.bo[dbuf].buftype = 'nowrite'
|
||||
vim.bo[dbuf].modifiable = false
|
||||
end
|
||||
|
||||
if diffthis then
|
||||
vim.cmd(table.concat({
|
||||
'keepalt', opts.split or 'aboveleft',
|
||||
opts.vertical and 'vertical' or '',
|
||||
'diffsplit', bufname,
|
||||
}, ' '))
|
||||
else
|
||||
vim.cmd('edit ' .. bufname)
|
||||
end
|
||||
if diffthis then
|
||||
vim.cmd(table.concat({
|
||||
'keepalt',
|
||||
opts.split or 'aboveleft',
|
||||
opts.vertical and 'vertical' or '',
|
||||
'diffsplit',
|
||||
bufname,
|
||||
}, ' '))
|
||||
else
|
||||
vim.cmd('edit ' .. bufname)
|
||||
end
|
||||
end
|
||||
|
||||
M.diffthis = void(function(base, opts)
|
||||
if vim.wo.diff then
|
||||
return
|
||||
end
|
||||
if vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
|
||||
local cwin = api.nvim_get_current_win()
|
||||
if not base and bcache.git_obj.has_conflicts then
|
||||
run(':2', true, opts)
|
||||
api.nvim_set_current_win(cwin)
|
||||
opts.split = 'belowright'
|
||||
run(':3', true, opts)
|
||||
else
|
||||
run(base, true, opts)
|
||||
end
|
||||
api.nvim_set_current_win(cwin)
|
||||
local cwin = api.nvim_get_current_win()
|
||||
if not base and bcache.git_obj.has_conflicts then
|
||||
run(':2', true, opts)
|
||||
api.nvim_set_current_win(cwin)
|
||||
opts.split = 'belowright'
|
||||
run(':3', true, opts)
|
||||
else
|
||||
run(base, true, opts)
|
||||
end
|
||||
api.nvim_set_current_win(cwin)
|
||||
end)
|
||||
|
||||
M.show = void(function(base)
|
||||
run(base, false)
|
||||
run(base, false)
|
||||
end)
|
||||
|
||||
local function should_reload(bufnr)
|
||||
if not vim.bo[bufnr].modified then
|
||||
return true
|
||||
end
|
||||
local response
|
||||
while not vim.tbl_contains({ 'O', 'L' }, response) do
|
||||
response = input({
|
||||
prompt = 'Warning: The git index has changed and the buffer was changed as well. [O]K, (L)oad File:',
|
||||
})
|
||||
end
|
||||
return response == 'L'
|
||||
if not vim.bo[bufnr].modified then
|
||||
return true
|
||||
end
|
||||
local response
|
||||
while not vim.tbl_contains({ 'O', 'L' }, response) do
|
||||
response = input({
|
||||
prompt = 'Warning: The git index has changed and the buffer was changed as well. [O]K, (L)oad File:',
|
||||
})
|
||||
end
|
||||
return response == 'L'
|
||||
end
|
||||
|
||||
-- This function needs to be throttled as there is a call to vim.ui.input
|
||||
M.update = throttle_by_id(void(function(bufnr)
|
||||
if not vim.wo.diff then
|
||||
return
|
||||
end
|
||||
if not vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
-- Note this will be the bufname for the currently set base
|
||||
-- which are the only ones we want to update
|
||||
local bufname = bcache:get_rev_bufname()
|
||||
-- Note this will be the bufname for the currently set base
|
||||
-- which are the only ones we want to update
|
||||
local bufname = bcache:get_rev_bufname()
|
||||
|
||||
for _, w in ipairs(api.nvim_list_wins()) do
|
||||
if api.nvim_win_is_valid(w) then
|
||||
local b = api.nvim_win_get_buf(w)
|
||||
local bname = api.nvim_buf_get_name(b)
|
||||
if bname == bufname or vim.startswith(bname, 'fugitive://') then
|
||||
if should_reload(b) then
|
||||
api.nvim_buf_call(b, function()
|
||||
vim.cmd('doautocmd BufReadCmd')
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
end
|
||||
end
|
||||
for _, w in ipairs(api.nvim_list_wins()) do
|
||||
if api.nvim_win_is_valid(w) then
|
||||
local b = api.nvim_win_get_buf(w)
|
||||
local bname = api.nvim_buf_get_name(b)
|
||||
if bname == bufname or vim.startswith(bname, 'fugitive://') then
|
||||
if should_reload(b) then
|
||||
api.nvim_buf_call(b, function()
|
||||
vim.cmd('doautocmd BufReadCmd')
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end))
|
||||
|
||||
return M
|
||||
|
953
lua/gitsigns/git.lua
generated
953
lua/gitsigns/git.lua
generated
File diff suppressed because it is too large
Load Diff
414
lua/gitsigns/highlight.lua
generated
414
lua/gitsigns/highlight.lua
generated
@ -1,223 +1,307 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
-- Use array of dict so we can iterate deterministically
|
||||
-- Export for docgen
|
||||
M.hls = {
|
||||
{ GitSignsAdd = { 'GitGutterAdd', 'SignifySignAdd', 'DiffAddedGutter', 'diffAdded', 'DiffAdd',
|
||||
desc = "Used for the text of 'add' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAdd = {
|
||||
'GitGutterAdd',
|
||||
'SignifySignAdd',
|
||||
'DiffAddedGutter',
|
||||
'diffAdded',
|
||||
'DiffAdd',
|
||||
desc = "Used for the text of 'add' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChange = { 'GitGutterChange', 'SignifySignChange', 'DiffModifiedGutter', 'diffChanged', 'DiffChange',
|
||||
desc = "Used for the text of 'change' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChange = {
|
||||
'GitGutterChange',
|
||||
'SignifySignChange',
|
||||
'DiffModifiedGutter',
|
||||
'diffChanged',
|
||||
'DiffChange',
|
||||
desc = "Used for the text of 'change' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDelete = { 'GitGutterDelete', 'SignifySignDelete', 'DiffRemovedGutter', 'diffRemoved', 'DiffDelete',
|
||||
desc = "Used for the text of 'delete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDelete = {
|
||||
'GitGutterDelete',
|
||||
'SignifySignDelete',
|
||||
'DiffRemovedGutter',
|
||||
'diffRemoved',
|
||||
'DiffDelete',
|
||||
desc = "Used for the text of 'delete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangedelete = { 'GitSignsChange',
|
||||
desc = "Used for the text of 'changedelete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangedelete = {
|
||||
'GitSignsChange',
|
||||
desc = "Used for the text of 'changedelete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsTopdelete = { 'GitSignsDelete',
|
||||
desc = "Used for the text of 'topdelete' signs.",
|
||||
}, },
|
||||
{ GitSignsTopdelete = { 'GitSignsDelete', desc = "Used for the text of 'topdelete' signs." } },
|
||||
|
||||
{ GitSignsUntracked = { 'GitSignsAdd',
|
||||
desc = "Used for the text of 'untracked' signs.",
|
||||
}, },
|
||||
{ GitSignsUntracked = { 'GitSignsAdd', desc = "Used for the text of 'untracked' signs." } },
|
||||
|
||||
{ GitSignsAddNr = { 'GitGutterAddLineNr', 'GitSignsAdd',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'add' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAddNr = {
|
||||
'GitGutterAddLineNr',
|
||||
'GitSignsAdd',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'add' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangeNr = { 'GitGutterChangeLineNr', 'GitSignsChange',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'change' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangeNr = {
|
||||
'GitGutterChangeLineNr',
|
||||
'GitSignsChange',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'change' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDeleteNr = { 'GitGutterDeleteLineNr', 'GitSignsDelete',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'delete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDeleteNr = {
|
||||
'GitGutterDeleteLineNr',
|
||||
'GitSignsDelete',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'delete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangedeleteNr = { 'GitSignsChangeNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'changedelete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangedeleteNr = {
|
||||
'GitSignsChangeNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'changedelete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsTopdeleteNr = { 'GitSignsDeleteNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'topdelete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsTopdeleteNr = {
|
||||
'GitSignsDeleteNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'topdelete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsUntrackedNr = { 'GitSignsAddNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'untracked' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsUntrackedNr = {
|
||||
'GitSignsAddNr',
|
||||
desc = "Used for number column (when `config.numhl == true`) of 'untracked' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsAddLn = { 'GitGutterAddLine', 'SignifyLineAdd', 'DiffAdd',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'add' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAddLn = {
|
||||
'GitGutterAddLine',
|
||||
'SignifyLineAdd',
|
||||
'DiffAdd',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'add' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangeLn = { 'GitGutterChangeLine', 'SignifyLineChange', 'DiffChange',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'change' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangeLn = {
|
||||
'GitGutterChangeLine',
|
||||
'SignifyLineChange',
|
||||
'DiffChange',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'change' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangedeleteLn = { 'GitSignsChangeLn',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'changedelete' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangedeleteLn = {
|
||||
'GitSignsChangeLn',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'changedelete' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsUntrackedLn = { 'GitSignsAddLn',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'untracked' signs.",
|
||||
}, },
|
||||
{
|
||||
GitSignsUntrackedLn = {
|
||||
'GitSignsAddLn',
|
||||
desc = "Used for buffer line (when `config.linehl == true`) of 'untracked' signs.",
|
||||
},
|
||||
},
|
||||
|
||||
-- Don't set GitSignsDeleteLn by default
|
||||
-- {GitSignsDeleteLn = {}},
|
||||
-- Don't set GitSignsDeleteLn by default
|
||||
-- {GitSignsDeleteLn = {}},
|
||||
|
||||
{ GitSignsStagedAdd = { 'GitSignsAdd', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChange = { 'GitSignsChange', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDelete = { 'GitSignsDelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedelete = { 'GitSignsChangedelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdelete = { 'GitSignsTopdelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedAddNr = { 'GitSignsAddNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangeNr = { 'GitSignsChangeNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDeleteNr = { 'GitSignsDeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedeleteNr = { 'GitSignsChangedeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdeleteNr = { 'GitSignsTopdeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedAddLn = { 'GitSignsAddLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangeLn = { 'GitSignsChangeLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDeleteLn = { 'GitSignsDeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedeleteLn = { 'GitSignsChangedeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdeleteLn = { 'GitSignsTopdeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedAdd = { 'GitSignsAdd', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChange = { 'GitSignsChange', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDelete = { 'GitSignsDelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedelete = { 'GitSignsChangedelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdelete = { 'GitSignsTopdelete', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedAddNr = { 'GitSignsAddNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangeNr = { 'GitSignsChangeNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDeleteNr = { 'GitSignsDeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedeleteNr = { 'GitSignsChangedeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdeleteNr = { 'GitSignsTopdeleteNr', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedAddLn = { 'GitSignsAddLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangeLn = { 'GitSignsChangeLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedDeleteLn = { 'GitSignsDeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedChangedeleteLn = { 'GitSignsChangedeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
{ GitSignsStagedTopdeleteLn = { 'GitSignsTopdeleteLn', fg_factor = 0.5, hidden = true } },
|
||||
|
||||
{ GitSignsAddPreview = { 'GitGutterAddLine', 'SignifyLineAdd', 'DiffAdd',
|
||||
desc = "Used for added lines in previews.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAddPreview = {
|
||||
'GitGutterAddLine',
|
||||
'SignifyLineAdd',
|
||||
'DiffAdd',
|
||||
desc = 'Used for added lines in previews.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDeletePreview = { 'GitGutterDeleteLine', 'SignifyLineDelete', 'DiffDelete',
|
||||
desc = "Used for deleted lines in previews.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDeletePreview = {
|
||||
'GitGutterDeleteLine',
|
||||
'SignifyLineDelete',
|
||||
'DiffDelete',
|
||||
desc = 'Used for deleted lines in previews.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsCurrentLineBlame = { 'NonText',
|
||||
desc = "Used for current line blame.",
|
||||
}, },
|
||||
{ GitSignsCurrentLineBlame = { 'NonText', desc = 'Used for current line blame.' } },
|
||||
|
||||
{ GitSignsAddInline = { 'TermCursor',
|
||||
desc = "Used for added word diff regions in inline previews.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAddInline = {
|
||||
'TermCursor',
|
||||
desc = 'Used for added word diff regions in inline previews.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDeleteInline = { 'TermCursor',
|
||||
desc = "Used for deleted word diff regions in inline previews.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDeleteInline = {
|
||||
'TermCursor',
|
||||
desc = 'Used for deleted word diff regions in inline previews.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangeInline = { 'TermCursor',
|
||||
desc = "Used for changed word diff regions in inline previews.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangeInline = {
|
||||
'TermCursor',
|
||||
desc = 'Used for changed word diff regions in inline previews.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsAddLnInline = { 'GitSignsAddInline',
|
||||
desc = "Used for added word diff regions when `config.word_diff == true`.",
|
||||
}, },
|
||||
{
|
||||
GitSignsAddLnInline = {
|
||||
'GitSignsAddInline',
|
||||
desc = 'Used for added word diff regions when `config.word_diff == true`.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsChangeLnInline = { 'GitSignsChangeInline',
|
||||
desc = "Used for changed word diff regions when `config.word_diff == true`.",
|
||||
}, },
|
||||
{
|
||||
GitSignsChangeLnInline = {
|
||||
'GitSignsChangeInline',
|
||||
desc = 'Used for changed word diff regions when `config.word_diff == true`.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDeleteLnInline = { 'GitSignsDeleteInline',
|
||||
desc = "Used for deleted word diff regions when `config.word_diff == true`.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDeleteLnInline = {
|
||||
'GitSignsDeleteInline',
|
||||
desc = 'Used for deleted word diff regions when `config.word_diff == true`.',
|
||||
},
|
||||
},
|
||||
|
||||
-- Currently unused
|
||||
-- {GitSignsAddLnVirtLn = {'GitSignsAddLn'}},
|
||||
-- {GitSignsChangeVirtLn = {'GitSignsChangeLn'}},
|
||||
-- {GitSignsAddLnVirtLnInLine = {'GitSignsAddLnInline', }},
|
||||
-- {GitSignsChangeVirtLnInLine = {'GitSignsChangeLnInline', }},
|
||||
-- Currently unused
|
||||
-- {GitSignsAddLnVirtLn = {'GitSignsAddLn'}},
|
||||
-- {GitSignsChangeVirtLn = {'GitSignsChangeLn'}},
|
||||
-- {GitSignsAddLnVirtLnInLine = {'GitSignsAddLnInline', }},
|
||||
-- {GitSignsChangeVirtLnInLine = {'GitSignsChangeLnInline', }},
|
||||
|
||||
{ GitSignsDeleteVirtLn = { 'GitGutterDeleteLine', 'SignifyLineDelete', 'DiffDelete',
|
||||
desc = "Used for deleted lines shown by inline `preview_hunk_inline()` or `show_deleted()`.",
|
||||
}, },
|
||||
{
|
||||
GitSignsDeleteVirtLn = {
|
||||
'GitGutterDeleteLine',
|
||||
'SignifyLineDelete',
|
||||
'DiffDelete',
|
||||
desc = 'Used for deleted lines shown by inline `preview_hunk_inline()` or `show_deleted()`.',
|
||||
},
|
||||
},
|
||||
|
||||
{ GitSignsDeleteVirtLnInLine = { 'GitSignsDeleteLnInline',
|
||||
desc = "Used for word diff regions in lines shown by inline `preview_hunk_inline()` or `show_deleted()`.",
|
||||
}, },
|
||||
|
||||
{ GitSignsVirtLnum = { 'GitSignsDeleteVirtLn',
|
||||
desc = 'Used for line numbers in inline hunks previews.',
|
||||
}, },
|
||||
{
|
||||
GitSignsDeleteVirtLnInLine = {
|
||||
'GitSignsDeleteLnInline',
|
||||
desc = 'Used for word diff regions in lines shown by inline `preview_hunk_inline()` or `show_deleted()`.',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
GitSignsVirtLnum = {
|
||||
'GitSignsDeleteVirtLn',
|
||||
desc = 'Used for line numbers in inline hunks previews.',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local function is_hl_set(hl_name)
|
||||
-- TODO: this only works with `set termguicolors`
|
||||
local exists, hl = pcall(vim.api.nvim_get_hl_by_name, hl_name, true)
|
||||
local color = hl.foreground or hl.background or hl.reverse
|
||||
return exists and color ~= nil
|
||||
-- TODO: this only works with `set termguicolors`
|
||||
local exists, hl = pcall(vim.api.nvim_get_hl_by_name, hl_name, true)
|
||||
local color = hl.foreground or hl.background or hl.reverse
|
||||
return exists and color ~= nil
|
||||
end
|
||||
|
||||
local function cmul(x, factor)
|
||||
if not x or factor == 1 then
|
||||
return x
|
||||
end
|
||||
if not x or factor == 1 then
|
||||
return x
|
||||
end
|
||||
|
||||
local r = math.floor(x / 2 ^ 16)
|
||||
local x1 = x - (r * 2 ^ 16)
|
||||
local g = math.floor(x1 / 2 ^ 8)
|
||||
local b = math.floor(x1 - (g * 2 ^ 8))
|
||||
return math.floor(math.floor(r * factor) * 2 ^ 16 + math.floor(g * factor) * 2 ^ 8 + math.floor(b * factor))
|
||||
local r = math.floor(x / 2 ^ 16)
|
||||
local x1 = x - (r * 2 ^ 16)
|
||||
local g = math.floor(x1 / 2 ^ 8)
|
||||
local b = math.floor(x1 - (g * 2 ^ 8))
|
||||
return math.floor(
|
||||
math.floor(r * factor) * 2 ^ 16 + math.floor(g * factor) * 2 ^ 8 + math.floor(b * factor)
|
||||
)
|
||||
end
|
||||
|
||||
local function dprintf(fmt, ...)
|
||||
require('gitsigns.debug.log').dprintf(fmt, ...)
|
||||
require('gitsigns.debug.log').dprintf(fmt, ...)
|
||||
end
|
||||
|
||||
local function derive(hl, hldef)
|
||||
for _, d in ipairs(hldef) do
|
||||
if is_hl_set(d) then
|
||||
dprintf('Deriving %s from %s', hl, d)
|
||||
if hldef.fg_factor or hldef.bg_factor then
|
||||
hldef.fg_factor = hldef.fg_factor or 1
|
||||
hldef.bg_factor = hldef.bg_factor or 1
|
||||
local dh = vim.api.nvim_get_hl_by_name(d, true)
|
||||
vim.api.nvim_set_hl(0, hl, {
|
||||
default = true,
|
||||
fg = cmul(dh.foreground, hldef.fg_factor),
|
||||
bg = cmul(dh.background, hldef.bg_factor),
|
||||
})
|
||||
else
|
||||
vim.api.nvim_set_hl(0, hl, { default = true, link = d })
|
||||
end
|
||||
return
|
||||
for _, d in ipairs(hldef) do
|
||||
if is_hl_set(d) then
|
||||
dprintf('Deriving %s from %s', hl, d)
|
||||
if hldef.fg_factor or hldef.bg_factor then
|
||||
hldef.fg_factor = hldef.fg_factor or 1
|
||||
hldef.bg_factor = hldef.bg_factor or 1
|
||||
local dh = vim.api.nvim_get_hl_by_name(d, true)
|
||||
vim.api.nvim_set_hl(0, hl, {
|
||||
default = true,
|
||||
fg = cmul(dh.foreground, hldef.fg_factor),
|
||||
bg = cmul(dh.background, hldef.bg_factor),
|
||||
})
|
||||
else
|
||||
vim.api.nvim_set_hl(0, hl, { default = true, link = d })
|
||||
end
|
||||
end
|
||||
if hldef[1] and not hldef.bg_factor and not hldef.fg_factor then
|
||||
-- No fallback found which is set. Just link to the first fallback
|
||||
-- if there are no modifiers
|
||||
dprintf('Deriving %s from %s', hl, hldef[1])
|
||||
vim.api.nvim_set_hl(0, hl, { default = true, link = hldef[1] })
|
||||
else
|
||||
dprintf('Could not derive %s', hl)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
if hldef[1] and not hldef.bg_factor and not hldef.fg_factor then
|
||||
-- No fallback found which is set. Just link to the first fallback
|
||||
-- if there are no modifiers
|
||||
dprintf('Deriving %s from %s', hl, hldef[1])
|
||||
vim.api.nvim_set_hl(0, hl, { default = true, link = hldef[1] })
|
||||
else
|
||||
dprintf('Could not derive %s', hl)
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup a GitSign* highlight by deriving it from other potentially present
|
||||
-- highlights.
|
||||
M.setup_highlights = function()
|
||||
for _, hlg in ipairs(M.hls) do
|
||||
for hl, hldef in pairs(hlg) do
|
||||
if is_hl_set(hl) then
|
||||
-- Already defined
|
||||
dprintf('Highlight %s is already defined', hl)
|
||||
else
|
||||
derive(hl, hldef)
|
||||
end
|
||||
for _, hlg in ipairs(M.hls) do
|
||||
for hl, hldef in pairs(hlg) do
|
||||
if is_hl_set(hl) then
|
||||
-- Already defined
|
||||
dprintf('Highlight %s is already defined', hl)
|
||||
else
|
||||
derive(hl, hldef)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
540
lua/gitsigns/hunks.lua
generated
540
lua/gitsigns/hunks.lua
generated
@ -29,36 +29,9 @@ local min, max = math.min, math.max
|
||||
--- @field added Gitsigns.Hunk.Node
|
||||
--- @field removed Gitsigns.Hunk.Node
|
||||
|
||||
local M = {Node = {}, Hunk = {}, Hunk_Public = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- For internal use
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = { Node = {}, Hunk = {}, Hunk_Public = {} }
|
||||
|
||||
-- For internal use
|
||||
|
||||
local Hunk = M.Hunk
|
||||
|
||||
@ -68,18 +41,19 @@ local Hunk = M.Hunk
|
||||
--- @param new_count integer
|
||||
--- @return Gitsigns.Hunk.Hunk
|
||||
function M.create_hunk(old_start, old_count, new_start, new_count)
|
||||
return {
|
||||
removed = { start = old_start, count = old_count, lines = {} },
|
||||
added = { start = new_start, count = new_count, lines = {} },
|
||||
head = ('@@ -%d%s +%d%s @@'):format(
|
||||
old_start, old_count > 0 and ',' .. old_count or '',
|
||||
new_start, new_count > 0 and ',' .. new_count or ''),
|
||||
return {
|
||||
removed = { start = old_start, count = old_count, lines = {} },
|
||||
added = { start = new_start, count = new_count, lines = {} },
|
||||
head = ('@@ -%d%s +%d%s @@'):format(
|
||||
old_start,
|
||||
old_count > 0 and ',' .. old_count or '',
|
||||
new_start,
|
||||
new_count > 0 and ',' .. new_count or ''
|
||||
),
|
||||
|
||||
vend = new_start + math.max(new_count - 1, 0),
|
||||
type = new_count == 0 and 'delete' or
|
||||
old_count == 0 and 'add' or
|
||||
'change',
|
||||
}
|
||||
vend = new_start + math.max(new_count - 1, 0),
|
||||
type = new_count == 0 and 'delete' or old_count == 0 and 'add' or 'change',
|
||||
}
|
||||
end
|
||||
|
||||
--- @param hunks Gitsigns.Hunk.Hunk[]
|
||||
@ -87,97 +61,100 @@ end
|
||||
--- @param bot integer
|
||||
--- @return Gitsigns.Hunk.Hunk
|
||||
function M.create_partial_hunk(hunks, top, bot)
|
||||
local pretop, precount = top, bot - top + 1
|
||||
for _, h in ipairs(hunks) do
|
||||
local added_in_hunk = h.added.count - h.removed.count
|
||||
local pretop, precount = top, bot - top + 1
|
||||
for _, h in ipairs(hunks) do
|
||||
local added_in_hunk = h.added.count - h.removed.count
|
||||
|
||||
local added_in_range = 0
|
||||
if h.added.start >= top and h.vend <= bot then
|
||||
-- Range contains hunk
|
||||
added_in_range = added_in_hunk
|
||||
else
|
||||
local added_above_bot = max(0, bot + 1 - (h.added.start + h.removed.count))
|
||||
local added_above_top = max(0, top - (h.added.start + h.removed.count))
|
||||
local added_in_range = 0
|
||||
if h.added.start >= top and h.vend <= bot then
|
||||
-- Range contains hunk
|
||||
added_in_range = added_in_hunk
|
||||
else
|
||||
local added_above_bot = max(0, bot + 1 - (h.added.start + h.removed.count))
|
||||
local added_above_top = max(0, top - (h.added.start + h.removed.count))
|
||||
|
||||
if h.added.start >= top and h.added.start <= bot then
|
||||
-- Range top intersects hunk
|
||||
added_in_range = added_above_bot
|
||||
elseif h.vend >= top and h.vend <= bot then
|
||||
-- Range bottom intersects hunk
|
||||
added_in_range = added_in_hunk - added_above_top
|
||||
pretop = pretop - added_above_top
|
||||
elseif h.added.start <= top and h.vend >= bot then
|
||||
-- Range within hunk
|
||||
added_in_range = added_above_bot - added_above_top
|
||||
pretop = pretop - added_above_top
|
||||
end
|
||||
|
||||
if top > h.vend then
|
||||
pretop = pretop - added_in_hunk
|
||||
end
|
||||
if h.added.start >= top and h.added.start <= bot then
|
||||
-- Range top intersects hunk
|
||||
added_in_range = added_above_bot
|
||||
elseif h.vend >= top and h.vend <= bot then
|
||||
-- Range bottom intersects hunk
|
||||
added_in_range = added_in_hunk - added_above_top
|
||||
pretop = pretop - added_above_top
|
||||
elseif h.added.start <= top and h.vend >= bot then
|
||||
-- Range within hunk
|
||||
added_in_range = added_above_bot - added_above_top
|
||||
pretop = pretop - added_above_top
|
||||
end
|
||||
|
||||
precount = precount - added_in_range
|
||||
end
|
||||
if top > h.vend then
|
||||
pretop = pretop - added_in_hunk
|
||||
end
|
||||
end
|
||||
|
||||
if precount == 0 then
|
||||
pretop = pretop - 1
|
||||
end
|
||||
precount = precount - added_in_range
|
||||
end
|
||||
|
||||
return M.create_hunk(pretop, precount, top, bot - top + 1)
|
||||
if precount == 0 then
|
||||
pretop = pretop - 1
|
||||
end
|
||||
|
||||
return M.create_hunk(pretop, precount, top, bot - top + 1)
|
||||
end
|
||||
|
||||
--- @param hunk Gitsigns.Hunk.Hunk
|
||||
--- @param fileformat string
|
||||
--- @return string[]
|
||||
function M.patch_lines(hunk, fileformat)
|
||||
local lines = {} --- @type string[]
|
||||
for _, l in ipairs(hunk.removed.lines) do
|
||||
lines[#lines + 1] = '-' .. l
|
||||
end
|
||||
for _, l in ipairs(hunk.added.lines) do
|
||||
lines[#lines + 1] = '+' .. l
|
||||
end
|
||||
local lines = {} --- @type string[]
|
||||
for _, l in ipairs(hunk.removed.lines) do
|
||||
lines[#lines + 1] = '-' .. l
|
||||
end
|
||||
for _, l in ipairs(hunk.added.lines) do
|
||||
lines[#lines + 1] = '+' .. l
|
||||
end
|
||||
|
||||
if fileformat == 'dos' then
|
||||
lines = util.strip_cr(lines)
|
||||
end
|
||||
return lines
|
||||
if fileformat == 'dos' then
|
||||
lines = util.strip_cr(lines)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
--- @param line string
|
||||
--- @return Gitsigns.Hunk.Hunk
|
||||
function M.parse_diff_line(line)
|
||||
local diffkey = vim.trim(vim.split(line, '@@', true)[2])
|
||||
local diffkey = vim.trim(vim.split(line, '@@', true)[2])
|
||||
|
||||
-- diffKey: "-xx,n +yy"
|
||||
-- pre: {xx, n}, now: {yy}
|
||||
local pre, now = unpack(vim.tbl_map(function(s)
|
||||
return vim.split(string.sub(s, 2), ',')
|
||||
end, vim.split(diffkey, ' ')))
|
||||
-- diffKey: "-xx,n +yy"
|
||||
-- pre: {xx, n}, now: {yy}
|
||||
local pre, now = unpack(vim.tbl_map(function(s)
|
||||
return vim.split(string.sub(s, 2), ',')
|
||||
end, vim.split(diffkey, ' ')))
|
||||
|
||||
local hunk = M.create_hunk(
|
||||
tonumber(pre[1]), (tonumber(pre[2]) or 1),
|
||||
tonumber(now[1]), (tonumber(now[2]) or 1))
|
||||
local hunk = M.create_hunk(
|
||||
tonumber(pre[1]),
|
||||
(tonumber(pre[2]) or 1),
|
||||
tonumber(now[1]),
|
||||
(tonumber(now[2]) or 1)
|
||||
)
|
||||
|
||||
hunk.head = line
|
||||
hunk.head = line
|
||||
|
||||
return hunk
|
||||
return hunk
|
||||
end
|
||||
|
||||
--- @param hunk Gitsigns.Hunk.Hunk
|
||||
--- @return integer
|
||||
local function change_end(hunk)
|
||||
if hunk.added.count == 0 then
|
||||
-- delete
|
||||
return hunk.added.start
|
||||
elseif hunk.removed.count == 0 then
|
||||
-- add
|
||||
return hunk.added.start + hunk.added.count - 1
|
||||
else
|
||||
-- change
|
||||
return hunk.added.start + min(hunk.added.count, hunk.removed.count) - 1
|
||||
end
|
||||
if hunk.added.count == 0 then
|
||||
-- delete
|
||||
return hunk.added.start
|
||||
elseif hunk.removed.count == 0 then
|
||||
-- add
|
||||
return hunk.added.start + hunk.added.count - 1
|
||||
else
|
||||
-- change
|
||||
return hunk.added.start + min(hunk.added.count, hunk.removed.count) - 1
|
||||
end
|
||||
end
|
||||
|
||||
--- Calculate signs needed to be applied from a hunk for a specified line range.
|
||||
@ -187,47 +164,45 @@ end
|
||||
--- @param untracked boolean
|
||||
--- @return Gitsigns.Sign[]
|
||||
function M.calc_signs(hunk, min_lnum, max_lnum, untracked)
|
||||
assert(not untracked or hunk.type == 'add')
|
||||
min_lnum = min_lnum or 1
|
||||
max_lnum = max_lnum or math.huge
|
||||
local start, added, removed = hunk.added.start, hunk.added.count, hunk.removed.count
|
||||
assert(not untracked or hunk.type == 'add')
|
||||
min_lnum = min_lnum or 1
|
||||
max_lnum = max_lnum or math.huge
|
||||
local start, added, removed = hunk.added.start, hunk.added.count, hunk.removed.count
|
||||
|
||||
if hunk.type == 'delete' and start == 0 then
|
||||
if min_lnum <= 1 then
|
||||
-- topdelete signs get placed one row lower
|
||||
return { { type = 'topdelete', count = removed, lnum = 1 } }
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
if hunk.type == 'delete' and start == 0 then
|
||||
if min_lnum <= 1 then
|
||||
-- topdelete signs get placed one row lower
|
||||
return { { type = 'topdelete', count = removed, lnum = 1 } }
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
local signs = {}
|
||||
local signs = {}
|
||||
|
||||
local cend = change_end(hunk)
|
||||
local cend = change_end(hunk)
|
||||
|
||||
for lnum = max(start, min_lnum), min(cend, max_lnum) do
|
||||
local changedelete = hunk.type == 'change' and removed > added and lnum == cend
|
||||
for lnum = max(start, min_lnum), min(cend, max_lnum) do
|
||||
local changedelete = hunk.type == 'change' and removed > added and lnum == cend
|
||||
|
||||
signs[#signs + 1] = {
|
||||
type = changedelete and 'changedelete' or untracked and 'untracked' or hunk.type,
|
||||
count = lnum == start and (hunk.type == 'add' and added or removed),
|
||||
lnum = lnum,
|
||||
}
|
||||
end
|
||||
|
||||
if hunk.type == 'change' and added > removed and hunk.vend >= min_lnum and cend <= max_lnum then
|
||||
for lnum = max(cend, min_lnum), min(hunk.vend, max_lnum) do
|
||||
signs[#signs + 1] = {
|
||||
type = changedelete and 'changedelete' or
|
||||
untracked and 'untracked' or hunk.type,
|
||||
count = lnum == start and (hunk.type == 'add' and added or removed),
|
||||
lnum = lnum,
|
||||
type = 'add',
|
||||
count = lnum == hunk.vend and (added - removed),
|
||||
lnum = lnum,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if hunk.type == "change" and added > removed and
|
||||
hunk.vend >= min_lnum and cend <= max_lnum then
|
||||
for lnum = max(cend, min_lnum), min(hunk.vend, max_lnum) do
|
||||
signs[#signs + 1] = {
|
||||
type = 'add',
|
||||
count = lnum == hunk.vend and (added - removed),
|
||||
lnum = lnum,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return signs
|
||||
return signs
|
||||
end
|
||||
|
||||
--- @param relpath string
|
||||
@ -236,83 +211,86 @@ end
|
||||
--- @param invert boolean
|
||||
--- @return string[]
|
||||
function M.create_patch(relpath, hunks, mode_bits, invert)
|
||||
invert = invert or false
|
||||
invert = invert or false
|
||||
|
||||
local results = {
|
||||
string.format('diff --git a/%s b/%s', relpath, relpath),
|
||||
'index 000000..000000 ' .. mode_bits,
|
||||
'--- a/' .. relpath,
|
||||
'+++ b/' .. relpath,
|
||||
}
|
||||
local results = {
|
||||
string.format('diff --git a/%s b/%s', relpath, relpath),
|
||||
'index 000000..000000 ' .. mode_bits,
|
||||
'--- a/' .. relpath,
|
||||
'+++ b/' .. relpath,
|
||||
}
|
||||
|
||||
local offset = 0
|
||||
local offset = 0
|
||||
|
||||
for _, process_hunk in ipairs(hunks) do
|
||||
local start, pre_count, now_count =
|
||||
for _, process_hunk in ipairs(hunks) do
|
||||
local start, pre_count, now_count =
|
||||
process_hunk.removed.start, process_hunk.removed.count, process_hunk.added.count
|
||||
|
||||
if process_hunk.type == 'add' then
|
||||
start = start + 1
|
||||
end
|
||||
if process_hunk.type == 'add' then
|
||||
start = start + 1
|
||||
end
|
||||
|
||||
local pre_lines = process_hunk.removed.lines
|
||||
local now_lines = process_hunk.added.lines
|
||||
local pre_lines = process_hunk.removed.lines
|
||||
local now_lines = process_hunk.added.lines
|
||||
|
||||
if invert then
|
||||
pre_count, now_count = now_count, pre_count
|
||||
pre_lines, now_lines = now_lines, pre_lines
|
||||
end
|
||||
if invert then
|
||||
pre_count, now_count = now_count, pre_count
|
||||
pre_lines, now_lines = now_lines, pre_lines
|
||||
end
|
||||
|
||||
table.insert(results, string.format('@@ -%s,%s +%s,%s @@', start, pre_count, start + offset, now_count))
|
||||
for _, l in ipairs(pre_lines) do
|
||||
results[#results + 1] = '-' .. l
|
||||
end
|
||||
for _, l in ipairs(now_lines) do
|
||||
results[#results + 1] = '+' .. l
|
||||
end
|
||||
table.insert(
|
||||
results,
|
||||
string.format('@@ -%s,%s +%s,%s @@', start, pre_count, start + offset, now_count)
|
||||
)
|
||||
for _, l in ipairs(pre_lines) do
|
||||
results[#results + 1] = '-' .. l
|
||||
end
|
||||
for _, l in ipairs(now_lines) do
|
||||
results[#results + 1] = '+' .. l
|
||||
end
|
||||
|
||||
process_hunk.removed.start = start + offset
|
||||
offset = offset + (now_count - pre_count)
|
||||
end
|
||||
process_hunk.removed.start = start + offset
|
||||
offset = offset + (now_count - pre_count)
|
||||
end
|
||||
|
||||
return results
|
||||
return results
|
||||
end
|
||||
|
||||
--- @param hunks Gitsigns.Hunk.Hunk[]
|
||||
--- @return Gitsigns.StatusObj
|
||||
function M.get_summary(hunks)
|
||||
local status = { added = 0, changed = 0, removed = 0 }
|
||||
local status = { added = 0, changed = 0, removed = 0 }
|
||||
|
||||
for _, hunk in ipairs(hunks or {}) do
|
||||
if hunk.type == 'add' then
|
||||
status.added = status.added + hunk.added.count
|
||||
elseif hunk.type == 'delete' then
|
||||
status.removed = status.removed + hunk.removed.count
|
||||
elseif hunk.type == 'change' then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
local delta = min(add, remove)
|
||||
status.changed = status.changed + delta
|
||||
status.added = status.added + add - delta
|
||||
status.removed = status.removed + remove - delta
|
||||
end
|
||||
end
|
||||
for _, hunk in ipairs(hunks or {}) do
|
||||
if hunk.type == 'add' then
|
||||
status.added = status.added + hunk.added.count
|
||||
elseif hunk.type == 'delete' then
|
||||
status.removed = status.removed + hunk.removed.count
|
||||
elseif hunk.type == 'change' then
|
||||
local add, remove = hunk.added.count, hunk.removed.count
|
||||
local delta = min(add, remove)
|
||||
status.changed = status.changed + delta
|
||||
status.added = status.added + add - delta
|
||||
status.removed = status.removed + remove - delta
|
||||
end
|
||||
end
|
||||
|
||||
return status
|
||||
return status
|
||||
end
|
||||
|
||||
--- @param lnum integer
|
||||
--- @param hunks Gitsigns.Hunk.Hunk[]
|
||||
--- @return Gitsigns.Hunk.Hunk?, integer?
|
||||
function M.find_hunk(lnum, hunks)
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
if lnum == 1 and hunk.added.start == 0 and hunk.vend == 0 then
|
||||
return hunk, i
|
||||
end
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
if lnum == 1 and hunk.added.start == 0 and hunk.vend == 0 then
|
||||
return hunk, i
|
||||
end
|
||||
|
||||
if hunk.added.start <= lnum and hunk.vend >= lnum then
|
||||
return hunk, i
|
||||
end
|
||||
end
|
||||
if hunk.added.start <= lnum and hunk.vend >= lnum then
|
||||
return hunk, i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @param lnum integer
|
||||
@ -321,73 +299,73 @@ end
|
||||
--- @param wrap boolean
|
||||
--- @return Gitsigns.Hunk.Hunk, integer
|
||||
function M.find_nearest_hunk(lnum, hunks, forwards, wrap)
|
||||
local ret --- @type Gitsigns.Hunk.Hunk
|
||||
local index --- @type integer
|
||||
local distance = math.huge
|
||||
if forwards then
|
||||
for i = 1, #hunks do
|
||||
local hunk = hunks[i]
|
||||
local dist = hunk.added.start - lnum
|
||||
if dist > 0 and dist < distance then
|
||||
distance = dist
|
||||
ret = hunk
|
||||
index = i
|
||||
end
|
||||
local ret --- @type Gitsigns.Hunk.Hunk
|
||||
local index --- @type integer
|
||||
local distance = math.huge
|
||||
if forwards then
|
||||
for i = 1, #hunks do
|
||||
local hunk = hunks[i]
|
||||
local dist = hunk.added.start - lnum
|
||||
if dist > 0 and dist < distance then
|
||||
distance = dist
|
||||
ret = hunk
|
||||
index = i
|
||||
end
|
||||
else
|
||||
for i = #hunks, 1, -1 do
|
||||
local hunk = hunks[i]
|
||||
local dist = lnum - hunk.vend
|
||||
if dist > 0 and dist < distance then
|
||||
distance = dist
|
||||
ret = hunk
|
||||
index = i
|
||||
end
|
||||
end
|
||||
else
|
||||
for i = #hunks, 1, -1 do
|
||||
local hunk = hunks[i]
|
||||
local dist = lnum - hunk.vend
|
||||
if dist > 0 and dist < distance then
|
||||
distance = dist
|
||||
ret = hunk
|
||||
index = i
|
||||
end
|
||||
end
|
||||
if not ret and wrap then
|
||||
index = forwards and 1 or #hunks
|
||||
ret = hunks[index]
|
||||
end
|
||||
return ret, index
|
||||
end
|
||||
end
|
||||
if not ret and wrap then
|
||||
index = forwards and 1 or #hunks
|
||||
ret = hunks[index]
|
||||
end
|
||||
return ret, index
|
||||
end
|
||||
|
||||
--- @param a Gitsigns.Hunk.Hunk[]
|
||||
--- @param b Gitsigns.Hunk.Hunk[]
|
||||
--- @return boolean
|
||||
function M.compare_heads(a, b)
|
||||
if (a == nil) ~= (b == nil) then
|
||||
if (a == nil) ~= (b == nil) then
|
||||
return true
|
||||
elseif a and #a ~= #b then
|
||||
return true
|
||||
end
|
||||
for i, ah in ipairs(a or {}) do
|
||||
if b[i].head ~= ah.head then
|
||||
return true
|
||||
elseif a and #a ~= #b then
|
||||
return true
|
||||
end
|
||||
for i, ah in ipairs(a or {}) do
|
||||
if b[i].head ~= ah.head then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- @param a Gitsigns.Hunk.Hunk
|
||||
--- @param b Gitsigns.Hunk.Hunk
|
||||
--- @return boolean
|
||||
local function compare_new(a, b)
|
||||
if a.added.start ~= b.added.start then
|
||||
if a.added.start ~= b.added.start then
|
||||
return false
|
||||
end
|
||||
|
||||
if a.added.count ~= b.added.count then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, a.added.count do
|
||||
if a.added.lines[i] ~= b.added.lines[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if a.added.count ~= b.added.count then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, a.added.count do
|
||||
if a.added.lines[i] ~= b.added.lines[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
return true
|
||||
end
|
||||
|
||||
--- Return hunks in a using b's hunks as a filter. Only compare the 'new' section
|
||||
@ -417,56 +395,56 @@ end
|
||||
--- @param b Gitsigns.Hunk.Hunk[]
|
||||
--- @return Gitsigns.Hunk.Hunk[]?
|
||||
function M.filter_common(a, b)
|
||||
if not a and not b then
|
||||
return
|
||||
end
|
||||
if not a and not b then
|
||||
return
|
||||
end
|
||||
|
||||
a, b = a or {}, b or {}
|
||||
local max_iter = math.max(#a, #b)
|
||||
a, b = a or {}, b or {}
|
||||
local max_iter = math.max(#a, #b)
|
||||
|
||||
local a_i = 1
|
||||
local b_i = 1
|
||||
local a_i = 1
|
||||
local b_i = 1
|
||||
|
||||
--- @type Gitsigns.Hunk.Hunk[]
|
||||
local ret = {}
|
||||
--- @type Gitsigns.Hunk.Hunk[]
|
||||
local ret = {}
|
||||
|
||||
for _ = 1, max_iter do
|
||||
local a_h, b_h = a[a_i], b[b_i]
|
||||
for _ = 1, max_iter do
|
||||
local a_h, b_h = a[a_i], b[b_i]
|
||||
|
||||
if not a_h then
|
||||
-- Reached the end of a
|
||||
break
|
||||
if not a_h then
|
||||
-- Reached the end of a
|
||||
break
|
||||
end
|
||||
|
||||
if not b_h then
|
||||
-- Reached the end of b, add remainder of a
|
||||
for i = a_i, #a do
|
||||
ret[#ret + 1] = a[i]
|
||||
end
|
||||
break
|
||||
end
|
||||
|
||||
if not b_h then
|
||||
-- Reached the end of b, add remainder of a
|
||||
for i = a_i, #a do
|
||||
ret[#ret + 1] = a[i]
|
||||
end
|
||||
break
|
||||
if a_h.added.start > b_h.added.start then
|
||||
-- a pointer is ahead of b; increment b pointer
|
||||
b_i = b_i + 1
|
||||
elseif a_h.added.start < b_h.added.start then
|
||||
-- b pointer is ahead of a; add a_h to ret and increment a pointer
|
||||
ret[#ret + 1] = a_h
|
||||
a_i = a_i + 1
|
||||
else -- a_h.start == b_h.start
|
||||
-- a_h and b_h start on the same line, if hunks have the same changes then
|
||||
-- skip (filtered) otherwise add a_h to ret. Increment both hunk
|
||||
-- pointers
|
||||
-- TODO(lewis6991): Be smarter; if bh intercepts then break down ah.
|
||||
if not compare_new(a_h, b_h) then
|
||||
ret[#ret + 1] = a_h
|
||||
end
|
||||
a_i = a_i + 1
|
||||
b_i = b_i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if a_h.added.start > b_h.added.start then
|
||||
-- a pointer is ahead of b; increment b pointer
|
||||
b_i = b_i + 1
|
||||
elseif a_h.added.start < b_h.added.start then
|
||||
-- b pointer is ahead of a; add a_h to ret and increment a pointer
|
||||
ret[#ret + 1] = a_h
|
||||
a_i = a_i + 1
|
||||
else -- a_h.start == b_h.start
|
||||
-- a_h and b_h start on the same line, if hunks have the same changes then
|
||||
-- skip (filtered) otherwise add a_h to ret. Increment both hunk
|
||||
-- pointers
|
||||
-- TODO(lewis6991): Be smarter; if bh intercepts then break down ah.
|
||||
if not compare_new(a_h, b_h) then
|
||||
ret[#ret + 1] = a_h
|
||||
end
|
||||
a_i = a_i + 1
|
||||
b_i = b_i + 1
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
return ret
|
||||
end
|
||||
|
||||
return M
|
||||
|
760
lua/gitsigns/manager.lua
generated
760
lua/gitsigns/manager.lua
generated
@ -7,7 +7,7 @@ local CacheEntry = gs_cache.CacheEntry
|
||||
local cache = gs_cache.cache
|
||||
|
||||
local Signs = require('gitsigns.signs')
|
||||
local Status = require("gitsigns.status")
|
||||
local Status = require('gitsigns.status')
|
||||
|
||||
local debounce_trailing = require('gitsigns.debounce').debounce_trailing
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
@ -22,7 +22,7 @@ local util = require('gitsigns.util')
|
||||
local run_diff = require('gitsigns.diff')
|
||||
local uv = require('gitsigns.uv')
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
local gs_hunks = require('gitsigns.hunks')
|
||||
local Hunk = gs_hunks.Hunk
|
||||
|
||||
local config = require('gitsigns.config').config
|
||||
@ -34,155 +34,150 @@ local signs_staged
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local scheduler_if_buf_valid = awrap(function(buf, cb)
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
end, 2)
|
||||
|
||||
local function apply_win_signs0(bufnr, signs, hunks, top, bot, clear, untracked)
|
||||
if clear then
|
||||
signs:remove(bufnr) -- Remove all signs
|
||||
end
|
||||
if clear then
|
||||
signs:remove(bufnr) -- Remove all signs
|
||||
end
|
||||
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
-- To stop the sign column width changing too much, if there are signs to be
|
||||
-- added but none of them are visible in the window, then make sure to add at
|
||||
-- least one sign. Only do this on the first call after an update when we all
|
||||
-- the signs have been cleared.
|
||||
if clear and i == 1 then
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, hunk.added.start, hunk.added.start, untracked))
|
||||
end
|
||||
for i, hunk in ipairs(hunks or {}) do
|
||||
-- To stop the sign column width changing too much, if there are signs to be
|
||||
-- added but none of them are visible in the window, then make sure to add at
|
||||
-- least one sign. Only do this on the first call after an update when we all
|
||||
-- the signs have been cleared.
|
||||
if clear and i == 1 then
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, hunk.added.start, hunk.added.start, untracked))
|
||||
end
|
||||
|
||||
if top <= hunk.vend and bot >= hunk.added.start then
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, top, bot, untracked))
|
||||
end
|
||||
if hunk.added.start > bot then
|
||||
break
|
||||
end
|
||||
end
|
||||
if top <= hunk.vend and bot >= hunk.added.start then
|
||||
signs:add(bufnr, gs_hunks.calc_signs(hunk, top, bot, untracked))
|
||||
end
|
||||
if hunk.added.start > bot then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_win_signs(bufnr, top, bot, clear, untracked)
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
apply_win_signs0(bufnr, signs_normal, bcache.hunks, top, bot, clear, untracked)
|
||||
if signs_staged then
|
||||
apply_win_signs0(bufnr, signs_staged, bcache.hunks_staged, top, bot, clear, false)
|
||||
end
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
apply_win_signs0(bufnr, signs_normal, bcache.hunks, top, bot, clear, untracked)
|
||||
if signs_staged then
|
||||
apply_win_signs0(bufnr, signs_staged, bcache.hunks_staged, top, bot, clear, false)
|
||||
end
|
||||
end
|
||||
|
||||
M.on_lines = function(buf, first, last_orig, last_new)
|
||||
local bcache = cache[buf]
|
||||
if not bcache then
|
||||
dprint('Cache for buffer was nil. Detaching')
|
||||
return true
|
||||
end
|
||||
local bcache = cache[buf]
|
||||
if not bcache then
|
||||
dprint('Cache for buffer was nil. Detaching')
|
||||
return true
|
||||
end
|
||||
|
||||
signs_normal:on_lines(buf, first, last_orig, last_new)
|
||||
if signs_staged then
|
||||
signs_staged:on_lines(buf, first, last_orig, last_new)
|
||||
end
|
||||
signs_normal:on_lines(buf, first, last_orig, last_new)
|
||||
if signs_staged then
|
||||
signs_staged:on_lines(buf, first, last_orig, last_new)
|
||||
end
|
||||
|
||||
-- Signs in changed regions get invalidated so we need to force a redraw if
|
||||
-- any signs get removed.
|
||||
if bcache.hunks and signs_normal:contains(buf, first, last_new) then
|
||||
-- Signs in changed regions get invalidated so we need to force a redraw if
|
||||
-- any signs get removed.
|
||||
if bcache.hunks and signs_normal:contains(buf, first, last_new) then
|
||||
-- Force a sign redraw on the next update (fixes #521)
|
||||
bcache.force_next_update = true
|
||||
end
|
||||
|
||||
if signs_staged then
|
||||
if bcache.hunks_staged and signs_staged:contains(buf, first, last_new) then
|
||||
-- Force a sign redraw on the next update (fixes #521)
|
||||
bcache.force_next_update = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if signs_staged then
|
||||
if bcache.hunks_staged and signs_staged:contains(buf, first, last_new) then
|
||||
-- Force a sign redraw on the next update (fixes #521)
|
||||
bcache.force_next_update = true
|
||||
end
|
||||
end
|
||||
|
||||
M.update_debounced(buf, cache[buf])
|
||||
M.update_debounced(buf, cache[buf])
|
||||
end
|
||||
|
||||
local ns = api.nvim_create_namespace('gitsigns')
|
||||
|
||||
local function apply_word_diff(bufnr, row)
|
||||
-- Don't run on folded lines
|
||||
if vim.fn.foldclosed(row + 1) ~= -1 then
|
||||
return
|
||||
end
|
||||
-- Don't run on folded lines
|
||||
if vim.fn.foldclosed(row + 1) ~= -1 then
|
||||
return
|
||||
end
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
if not bcache or not bcache.hunks then
|
||||
return
|
||||
end
|
||||
if not bcache or not bcache.hunks then
|
||||
return
|
||||
end
|
||||
|
||||
local line = api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1]
|
||||
if not line then
|
||||
-- Invalid line
|
||||
return
|
||||
end
|
||||
local line = api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1]
|
||||
if not line then
|
||||
-- Invalid line
|
||||
return
|
||||
end
|
||||
|
||||
local lnum = row + 1
|
||||
local lnum = row + 1
|
||||
|
||||
local hunk = gs_hunks.find_hunk(lnum, bcache.hunks)
|
||||
if not hunk then
|
||||
-- No hunk at line
|
||||
return
|
||||
end
|
||||
local hunk = gs_hunks.find_hunk(lnum, bcache.hunks)
|
||||
if not hunk then
|
||||
-- No hunk at line
|
||||
return
|
||||
end
|
||||
|
||||
if hunk.added.count ~= hunk.removed.count then
|
||||
-- Only word diff if added count == removed
|
||||
return
|
||||
end
|
||||
if hunk.added.count ~= hunk.removed.count then
|
||||
-- Only word diff if added count == removed
|
||||
return
|
||||
end
|
||||
|
||||
local pos = lnum - hunk.added.start + 1
|
||||
local pos = lnum - hunk.added.start + 1
|
||||
|
||||
local added_line = hunk.added.lines[pos]
|
||||
local removed_line = hunk.removed.lines[pos]
|
||||
local added_line = hunk.added.lines[pos]
|
||||
local removed_line = hunk.removed.lines[pos]
|
||||
|
||||
local _, added_regions = require('gitsigns.diff_int').run_word_diff({ removed_line }, { added_line })
|
||||
local _, added_regions = require('gitsigns.diff_int').run_word_diff(
|
||||
{ removed_line },
|
||||
{ added_line }
|
||||
)
|
||||
|
||||
local cols = #line
|
||||
local cols = #line
|
||||
|
||||
for _, region in ipairs(added_regions) do
|
||||
local rtype, scol, ecol = region[2], region[3] - 1, region[4] - 1
|
||||
if ecol == scol then
|
||||
-- Make sure region is at least 1 column wide so deletes can be shown
|
||||
ecol = scol + 1
|
||||
end
|
||||
for _, region in ipairs(added_regions) do
|
||||
local rtype, scol, ecol = region[2], region[3] - 1, region[4] - 1
|
||||
if ecol == scol then
|
||||
-- Make sure region is at least 1 column wide so deletes can be shown
|
||||
ecol = scol + 1
|
||||
end
|
||||
|
||||
local hl_group = rtype == 'add' and 'GitSignsAddLnInline' or
|
||||
rtype == 'change' and 'GitSignsChangeLnInline' or
|
||||
'GitSignsDeleteLnInline'
|
||||
local hl_group = rtype == 'add' and 'GitSignsAddLnInline'
|
||||
or rtype == 'change' and 'GitSignsChangeLnInline'
|
||||
or 'GitSignsDeleteLnInline'
|
||||
|
||||
local opts = {
|
||||
ephemeral = true,
|
||||
priority = 1000,
|
||||
}
|
||||
local opts = {
|
||||
ephemeral = true,
|
||||
priority = 1000,
|
||||
}
|
||||
|
||||
if ecol > cols and ecol == scol + 1 then
|
||||
-- delete on last column, use virtual text instead
|
||||
opts.virt_text = { { ' ', hl_group } }
|
||||
opts.virt_text_pos = 'overlay'
|
||||
else
|
||||
opts.end_col = ecol
|
||||
opts.hl_group = hl_group
|
||||
end
|
||||
if ecol > cols and ecol == scol + 1 then
|
||||
-- delete on last column, use virtual text instead
|
||||
opts.virt_text = { { ' ', hl_group } }
|
||||
opts.virt_text_pos = 'overlay'
|
||||
else
|
||||
opts.end_col = ecol
|
||||
opts.hl_group = hl_group
|
||||
end
|
||||
|
||||
api.nvim_buf_set_extmark(bufnr, ns, row, scol, opts)
|
||||
api.nvim__buf_redraw_range(bufnr, row, row + 1)
|
||||
end
|
||||
api.nvim_buf_set_extmark(bufnr, ns, row, scol, opts)
|
||||
api.nvim__buf_redraw_range(bufnr, row, row + 1)
|
||||
end
|
||||
end
|
||||
|
||||
local ns_rm = api.nvim_create_namespace('gitsigns_removed')
|
||||
@ -190,182 +185,185 @@ local ns_rm = api.nvim_create_namespace('gitsigns_removed')
|
||||
local VIRT_LINE_LEN = 300
|
||||
|
||||
local function clear_deleted(bufnr)
|
||||
local marks = api.nvim_buf_get_extmarks(bufnr, ns_rm, 0, -1, {})
|
||||
for _, mark in ipairs(marks) do
|
||||
api.nvim_buf_del_extmark(bufnr, ns_rm, mark[1])
|
||||
end
|
||||
local marks = api.nvim_buf_get_extmarks(bufnr, ns_rm, 0, -1, {})
|
||||
for _, mark in ipairs(marks) do
|
||||
api.nvim_buf_del_extmark(bufnr, ns_rm, mark[1])
|
||||
end
|
||||
end
|
||||
|
||||
function M.show_deleted(bufnr, nsd, hunk)
|
||||
local virt_lines = {}
|
||||
local virt_lines = {}
|
||||
|
||||
for i, line in ipairs(hunk.removed.lines) do
|
||||
local vline = {}
|
||||
local last_ecol = 1
|
||||
for i, line in ipairs(hunk.removed.lines) do
|
||||
local vline = {}
|
||||
local last_ecol = 1
|
||||
|
||||
if config.word_diff then
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(
|
||||
{ hunk.removed.lines[i] }, { hunk.added.lines[i] })
|
||||
if config.word_diff then
|
||||
local regions = require('gitsigns.diff_int').run_word_diff(
|
||||
{ hunk.removed.lines[i] },
|
||||
{ hunk.added.lines[i] }
|
||||
)
|
||||
|
||||
for _, region in ipairs(regions) do
|
||||
local rline, scol, ecol = region[1], region[3], region[4]
|
||||
if rline > 1 then
|
||||
break
|
||||
end
|
||||
vline[#vline + 1] = { line:sub(last_ecol, scol - 1), 'GitSignsDeleteVirtLn' }
|
||||
vline[#vline + 1] = { line:sub(scol, ecol - 1), 'GitSignsDeleteVirtLnInline' }
|
||||
last_ecol = ecol
|
||||
end
|
||||
for _, region in ipairs(regions) do
|
||||
local rline, scol, ecol = region[1], region[3], region[4]
|
||||
if rline > 1 then
|
||||
break
|
||||
end
|
||||
vline[#vline + 1] = { line:sub(last_ecol, scol - 1), 'GitSignsDeleteVirtLn' }
|
||||
vline[#vline + 1] = { line:sub(scol, ecol - 1), 'GitSignsDeleteVirtLnInline' }
|
||||
last_ecol = ecol
|
||||
end
|
||||
end
|
||||
|
||||
if #line > 0 then
|
||||
vline[#vline + 1] = { line:sub(last_ecol, -1), 'GitSignsDeleteVirtLn' }
|
||||
end
|
||||
if #line > 0 then
|
||||
vline[#vline + 1] = { line:sub(last_ecol, -1), 'GitSignsDeleteVirtLn' }
|
||||
end
|
||||
|
||||
-- Add extra padding so the entire line is highlighted
|
||||
local padding = string.rep(' ', VIRT_LINE_LEN - #line)
|
||||
vline[#vline + 1] = { padding, 'GitSignsDeleteVirtLn' }
|
||||
-- Add extra padding so the entire line is highlighted
|
||||
local padding = string.rep(' ', VIRT_LINE_LEN - #line)
|
||||
vline[#vline + 1] = { padding, 'GitSignsDeleteVirtLn' }
|
||||
|
||||
virt_lines[i] = vline
|
||||
end
|
||||
virt_lines[i] = vline
|
||||
end
|
||||
|
||||
local topdelete = hunk.added.start == 0 and hunk.type == 'delete'
|
||||
local topdelete = hunk.added.start == 0 and hunk.type == 'delete'
|
||||
|
||||
local row = topdelete and 0 or hunk.added.start - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsd, row, -1, {
|
||||
virt_lines = virt_lines,
|
||||
-- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166
|
||||
virt_lines_above = hunk.type ~= 'delete' or topdelete,
|
||||
})
|
||||
local row = topdelete and 0 or hunk.added.start - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsd, row, -1, {
|
||||
virt_lines = virt_lines,
|
||||
-- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166
|
||||
virt_lines_above = hunk.type ~= 'delete' or topdelete,
|
||||
})
|
||||
end
|
||||
|
||||
function M.show_deleted_in_float(bufnr, nsd, hunk)
|
||||
local virt_lines = {}
|
||||
for i = 1, hunk.removed.count do
|
||||
virt_lines[i] = { { '', 'Normal' } }
|
||||
end
|
||||
local virt_lines = {}
|
||||
for i = 1, hunk.removed.count do
|
||||
virt_lines[i] = { { '', 'Normal' } }
|
||||
end
|
||||
|
||||
local topdelete = hunk.added.start == 0 and hunk.type == 'delete'
|
||||
local virt_lines_above = hunk.type ~= 'delete' or topdelete
|
||||
local topdelete = hunk.added.start == 0 and hunk.type == 'delete'
|
||||
local virt_lines_above = hunk.type ~= 'delete' or topdelete
|
||||
|
||||
local row = topdelete and 0 or hunk.added.start - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsd, row, -1, {
|
||||
virt_lines = virt_lines,
|
||||
-- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166
|
||||
virt_lines_above = virt_lines_above,
|
||||
})
|
||||
local row = topdelete and 0 or hunk.added.start - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsd, row, -1, {
|
||||
virt_lines = virt_lines,
|
||||
-- TODO(lewis6991): Note virt_lines_above doesn't work on row 0 neovim/neovim#16166
|
||||
virt_lines_above = virt_lines_above,
|
||||
})
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
local pbufnr = api.nvim_create_buf(false, true)
|
||||
api.nvim_buf_set_lines(pbufnr, 0, -1, false, bcache.compare_text)
|
||||
local bcache = cache[bufnr]
|
||||
local pbufnr = api.nvim_create_buf(false, true)
|
||||
api.nvim_buf_set_lines(pbufnr, 0, -1, false, bcache.compare_text)
|
||||
|
||||
local cwin = api.nvim_get_current_win()
|
||||
local width = api.nvim_win_get_width(0)
|
||||
local cwin = api.nvim_get_current_win()
|
||||
local width = api.nvim_win_get_width(0)
|
||||
|
||||
local opts = {
|
||||
relative = 'win',
|
||||
win = cwin,
|
||||
width = width,
|
||||
height = hunk.removed.count,
|
||||
anchor = 'SW',
|
||||
bufpos = { hunk.added.start, 0 },
|
||||
}
|
||||
local opts = {
|
||||
relative = 'win',
|
||||
win = cwin,
|
||||
width = width,
|
||||
height = hunk.removed.count,
|
||||
anchor = 'SW',
|
||||
bufpos = { hunk.added.start, 0 },
|
||||
}
|
||||
|
||||
local bufpos_offset = virt_lines_above and not topdelete and 1 or 0
|
||||
opts.bufpos[1] = opts.bufpos[1] - bufpos_offset
|
||||
local bufpos_offset = virt_lines_above and not topdelete and 1 or 0
|
||||
opts.bufpos[1] = opts.bufpos[1] - bufpos_offset
|
||||
|
||||
local winid = api.nvim_open_win(pbufnr, false, opts)
|
||||
local winid = api.nvim_open_win(pbufnr, false, opts)
|
||||
|
||||
-- Align buffer text by accounting for differences in the statuscolumn
|
||||
local textoff = vim.fn.getwininfo(cwin)[1].textoff
|
||||
local ptextoff = vim.fn.getwininfo(winid)[1].textoff
|
||||
local col_offset = textoff - ptextoff
|
||||
-- Align buffer text by accounting for differences in the statuscolumn
|
||||
local textoff = vim.fn.getwininfo(cwin)[1].textoff
|
||||
local ptextoff = vim.fn.getwininfo(winid)[1].textoff
|
||||
local col_offset = textoff - ptextoff
|
||||
|
||||
if col_offset ~= 0 then
|
||||
opts.width = opts.width - col_offset
|
||||
opts.bufpos[2] = opts.bufpos[2] + col_offset
|
||||
api.nvim_win_set_config(winid, opts)
|
||||
end
|
||||
if col_offset ~= 0 then
|
||||
opts.width = opts.width - col_offset
|
||||
opts.bufpos[2] = opts.bufpos[2] + col_offset
|
||||
api.nvim_win_set_config(winid, opts)
|
||||
end
|
||||
|
||||
vim.bo[pbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[pbufnr].bufhidden = 'wipe'
|
||||
vim.wo[winid].scrolloff = 0
|
||||
vim.wo[winid].relativenumber = false
|
||||
vim.bo[pbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[pbufnr].bufhidden = 'wipe'
|
||||
vim.wo[winid].scrolloff = 0
|
||||
vim.wo[winid].relativenumber = false
|
||||
|
||||
api.nvim_win_call(winid, function()
|
||||
-- Expand folds
|
||||
vim.cmd('normal ' .. 'zR')
|
||||
api.nvim_win_call(winid, function()
|
||||
-- Expand folds
|
||||
vim.cmd('normal ' .. 'zR')
|
||||
|
||||
-- Navigate to hunk
|
||||
vim.cmd('normal ' .. tostring(hunk.removed.start) .. 'gg')
|
||||
vim.cmd('normal ' .. vim.api.nvim_replace_termcodes('z<CR>', true, false, true))
|
||||
end)
|
||||
-- Navigate to hunk
|
||||
vim.cmd('normal ' .. tostring(hunk.removed.start) .. 'gg')
|
||||
vim.cmd('normal ' .. vim.api.nvim_replace_termcodes('z<CR>', true, false, true))
|
||||
end)
|
||||
|
||||
-- Apply highlights
|
||||
-- Apply highlights
|
||||
|
||||
for i = hunk.removed.start, hunk.removed.start + hunk.removed.count do
|
||||
api.nvim_buf_set_extmark(pbufnr, nsd, i - 1, 0, {
|
||||
hl_group = 'GitSignsDeleteVirtLn',
|
||||
hl_eol = true,
|
||||
end_row = i,
|
||||
priority = 1000,
|
||||
})
|
||||
end
|
||||
for i = hunk.removed.start, hunk.removed.start + hunk.removed.count do
|
||||
api.nvim_buf_set_extmark(pbufnr, nsd, i - 1, 0, {
|
||||
hl_group = 'GitSignsDeleteVirtLn',
|
||||
hl_eol = true,
|
||||
end_row = i,
|
||||
priority = 1000,
|
||||
})
|
||||
end
|
||||
|
||||
local removed_regions =
|
||||
require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines)
|
||||
local removed_regions =
|
||||
require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines)
|
||||
|
||||
for _, region in ipairs(removed_regions) do
|
||||
local start_row = (hunk.removed.start - 1) + (region[1] - 1)
|
||||
local start_col = region[3] - 1
|
||||
local end_col = region[4] - 1
|
||||
api.nvim_buf_set_extmark(pbufnr, nsd, start_row, start_col, {
|
||||
hl_group = 'GitSignsDeleteVirtLnInline',
|
||||
end_col = end_col,
|
||||
end_row = start_row,
|
||||
priority = 1001,
|
||||
})
|
||||
end
|
||||
for _, region in ipairs(removed_regions) do
|
||||
local start_row = (hunk.removed.start - 1) + (region[1] - 1)
|
||||
local start_col = region[3] - 1
|
||||
local end_col = region[4] - 1
|
||||
api.nvim_buf_set_extmark(pbufnr, nsd, start_row, start_col, {
|
||||
hl_group = 'GitSignsDeleteVirtLnInline',
|
||||
end_col = end_col,
|
||||
end_row = start_row,
|
||||
priority = 1001,
|
||||
})
|
||||
end
|
||||
|
||||
return winid
|
||||
return winid
|
||||
end
|
||||
|
||||
function M.show_added(bufnr, nsw, hunk)
|
||||
local start_row = hunk.added.start - 1
|
||||
local start_row = hunk.added.start - 1
|
||||
|
||||
for offset = 0, hunk.added.count - 1 do
|
||||
local row = start_row + offset
|
||||
api.nvim_buf_set_extmark(bufnr, nsw, row, 0, {
|
||||
end_row = row + 1,
|
||||
hl_group = 'GitSignsAddPreview',
|
||||
hl_eol = true,
|
||||
priority = 1000,
|
||||
})
|
||||
end
|
||||
for offset = 0, hunk.added.count - 1 do
|
||||
local row = start_row + offset
|
||||
api.nvim_buf_set_extmark(bufnr, nsw, row, 0, {
|
||||
end_row = row + 1,
|
||||
hl_group = 'GitSignsAddPreview',
|
||||
hl_eol = true,
|
||||
priority = 1000,
|
||||
})
|
||||
end
|
||||
|
||||
local _, added_regions = require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines)
|
||||
local _, added_regions =
|
||||
require('gitsigns.diff_int').run_word_diff(hunk.removed.lines, hunk.added.lines)
|
||||
|
||||
for _, region in ipairs(added_regions) do
|
||||
local offset, rtype, scol, ecol = region[1] - 1, region[2], region[3] - 1, region[4] - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsw, start_row + offset, scol, {
|
||||
end_col = ecol,
|
||||
hl_group = rtype == 'add' and 'GitSignsAddInline' or
|
||||
rtype == 'change' and 'GitSignsChangeInline' or
|
||||
'GitSignsDeleteInline',
|
||||
priority = 1001,
|
||||
})
|
||||
end
|
||||
for _, region in ipairs(added_regions) do
|
||||
local offset, rtype, scol, ecol = region[1] - 1, region[2], region[3] - 1, region[4] - 1
|
||||
api.nvim_buf_set_extmark(bufnr, nsw, start_row + offset, scol, {
|
||||
end_col = ecol,
|
||||
hl_group = rtype == 'add' and 'GitSignsAddInline'
|
||||
or rtype == 'change' and 'GitSignsChangeInline'
|
||||
or 'GitSignsDeleteInline',
|
||||
priority = 1001,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function update_show_deleted(bufnr)
|
||||
local bcache = cache[bufnr]
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
clear_deleted(bufnr)
|
||||
if config.show_deleted then
|
||||
for _, hunk in ipairs(bcache.hunks or {}) do
|
||||
M.show_deleted(bufnr, ns_rm, hunk)
|
||||
end
|
||||
end
|
||||
clear_deleted(bufnr)
|
||||
if config.show_deleted then
|
||||
for _, hunk in ipairs(bcache.hunks or {}) do
|
||||
M.show_deleted(bufnr, ns_rm, hunk)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local update_cnt = 0
|
||||
@ -375,137 +373,142 @@ local update_cnt = 0
|
||||
-- whilst another one is in progress. If this happens then schedule another
|
||||
-- update after the current one has completed.
|
||||
M.update = throttle_by_id(function(bufnr, bcache)
|
||||
local __FUNC__ = 'update'
|
||||
bcache = bcache or cache[bufnr]
|
||||
if not bcache then
|
||||
eprint('Cache for buffer ' .. bufnr .. ' was nil')
|
||||
return
|
||||
end
|
||||
local old_hunks, old_hunks_staged = bcache.hunks, bcache.hunks_staged
|
||||
bcache.hunks, bcache.hunks_staged = nil, nil
|
||||
local __FUNC__ = 'update'
|
||||
bcache = bcache or cache[bufnr]
|
||||
if not bcache then
|
||||
eprint('Cache for buffer ' .. bufnr .. ' was nil')
|
||||
return
|
||||
end
|
||||
local old_hunks, old_hunks_staged = bcache.hunks, bcache.hunks_staged
|
||||
bcache.hunks, bcache.hunks_staged = nil, nil
|
||||
|
||||
scheduler_if_buf_valid(bufnr)
|
||||
local buftext = util.buf_lines(bufnr)
|
||||
local git_obj = bcache.git_obj
|
||||
scheduler_if_buf_valid(bufnr)
|
||||
local buftext = util.buf_lines(bufnr)
|
||||
local git_obj = bcache.git_obj
|
||||
|
||||
if not bcache.compare_text or config._refresh_staged_on_update then
|
||||
bcache.compare_text = git_obj:get_show_text(bcache:get_compare_rev())
|
||||
end
|
||||
if not bcache.compare_text or config._refresh_staged_on_update then
|
||||
bcache.compare_text = git_obj:get_show_text(bcache:get_compare_rev())
|
||||
end
|
||||
|
||||
bcache.hunks = run_diff(bcache.compare_text, buftext)
|
||||
bcache.hunks = run_diff(bcache.compare_text, buftext)
|
||||
|
||||
if config._signs_staged_enable then
|
||||
if not bcache.compare_text_head or config._refresh_staged_on_update then
|
||||
bcache.compare_text_head = git_obj:get_show_text(bcache:get_staged_compare_rev())
|
||||
end
|
||||
local hunks_head = run_diff(bcache.compare_text_head, buftext)
|
||||
bcache.hunks_staged = gs_hunks.filter_common(hunks_head, bcache.hunks)
|
||||
end
|
||||
if config._signs_staged_enable then
|
||||
if not bcache.compare_text_head or config._refresh_staged_on_update then
|
||||
bcache.compare_text_head = git_obj:get_show_text(bcache:get_staged_compare_rev())
|
||||
end
|
||||
local hunks_head = run_diff(bcache.compare_text_head, buftext)
|
||||
bcache.hunks_staged = gs_hunks.filter_common(hunks_head, bcache.hunks)
|
||||
end
|
||||
|
||||
scheduler_if_buf_valid(bufnr)
|
||||
scheduler_if_buf_valid(bufnr)
|
||||
|
||||
-- Note the decoration provider may have invalidated bcache.hunks at this
|
||||
-- point
|
||||
if bcache.force_next_update or gs_hunks.compare_heads(bcache.hunks, old_hunks) or
|
||||
gs_hunks.compare_heads(bcache.hunks_staged, old_hunks_staged) then
|
||||
-- Apply signs to the window. Other signs will be added by the decoration
|
||||
-- provider as they are drawn.
|
||||
apply_win_signs(bufnr, vim.fn.line('w0'), vim.fn.line('w$'), true, git_obj.object_name == nil)
|
||||
-- Note the decoration provider may have invalidated bcache.hunks at this
|
||||
-- point
|
||||
if
|
||||
bcache.force_next_update
|
||||
or gs_hunks.compare_heads(bcache.hunks, old_hunks)
|
||||
or gs_hunks.compare_heads(bcache.hunks_staged, old_hunks_staged)
|
||||
then
|
||||
-- Apply signs to the window. Other signs will be added by the decoration
|
||||
-- provider as they are drawn.
|
||||
apply_win_signs(bufnr, vim.fn.line('w0'), vim.fn.line('w$'), true, git_obj.object_name == nil)
|
||||
|
||||
update_show_deleted(bufnr)
|
||||
bcache.force_next_update = false
|
||||
update_show_deleted(bufnr)
|
||||
bcache.force_next_update = false
|
||||
|
||||
api.nvim_exec_autocmds('User', {
|
||||
pattern = 'GitSignsUpdate',
|
||||
modeline = false,
|
||||
})
|
||||
end
|
||||
api.nvim_exec_autocmds('User', {
|
||||
pattern = 'GitSignsUpdate',
|
||||
modeline = false,
|
||||
})
|
||||
end
|
||||
|
||||
local summary = gs_hunks.get_summary(bcache.hunks)
|
||||
summary.head = git_obj.repo.abbrev_head
|
||||
Status:update(bufnr, summary)
|
||||
local summary = gs_hunks.get_summary(bcache.hunks)
|
||||
summary.head = git_obj.repo.abbrev_head
|
||||
Status:update(bufnr, summary)
|
||||
|
||||
update_cnt = update_cnt + 1
|
||||
update_cnt = update_cnt + 1
|
||||
|
||||
dprintf('updates: %s, jobs: %s', update_cnt, subprocess.job_cnt)
|
||||
dprintf('updates: %s, jobs: %s', update_cnt, subprocess.job_cnt)
|
||||
end, true)
|
||||
|
||||
M.detach = function(bufnr, keep_signs)
|
||||
if not keep_signs then
|
||||
-- Remove all signs
|
||||
signs_normal:remove(bufnr)
|
||||
if signs_staged then
|
||||
signs_staged:remove(bufnr)
|
||||
end
|
||||
end
|
||||
if not keep_signs then
|
||||
-- Remove all signs
|
||||
signs_normal:remove(bufnr)
|
||||
if signs_staged then
|
||||
signs_staged:remove(bufnr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function handle_moved(bufnr, bcache, old_relpath)
|
||||
local git_obj = bcache.git_obj
|
||||
local do_update = false
|
||||
local git_obj = bcache.git_obj
|
||||
local do_update = false
|
||||
|
||||
local new_name = git_obj:has_moved()
|
||||
if new_name then
|
||||
dprintf('File moved to %s', new_name)
|
||||
git_obj.relpath = new_name
|
||||
if not git_obj.orig_relpath then
|
||||
git_obj.orig_relpath = old_relpath
|
||||
end
|
||||
local new_name = git_obj:has_moved()
|
||||
if new_name then
|
||||
dprintf('File moved to %s', new_name)
|
||||
git_obj.relpath = new_name
|
||||
if not git_obj.orig_relpath then
|
||||
git_obj.orig_relpath = old_relpath
|
||||
end
|
||||
do_update = true
|
||||
elseif git_obj.orig_relpath then
|
||||
local orig_file = git_obj.repo.toplevel .. util.path_sep .. git_obj.orig_relpath
|
||||
if git_obj:file_info(orig_file).relpath then
|
||||
dprintf('Moved file reset')
|
||||
git_obj.relpath = git_obj.orig_relpath
|
||||
git_obj.orig_relpath = nil
|
||||
do_update = true
|
||||
elseif git_obj.orig_relpath then
|
||||
local orig_file = git_obj.repo.toplevel .. util.path_sep .. git_obj.orig_relpath
|
||||
if git_obj:file_info(orig_file).relpath then
|
||||
dprintf('Moved file reset')
|
||||
git_obj.relpath = git_obj.orig_relpath
|
||||
git_obj.orig_relpath = nil
|
||||
do_update = true
|
||||
end
|
||||
else
|
||||
-- File removed from index, do nothing
|
||||
end
|
||||
end
|
||||
else
|
||||
-- File removed from index, do nothing
|
||||
end
|
||||
|
||||
if do_update then
|
||||
git_obj.file = git_obj.repo.toplevel .. util.path_sep .. git_obj.relpath
|
||||
bcache.file = git_obj.file
|
||||
git_obj:update_file_info()
|
||||
scheduler()
|
||||
if do_update then
|
||||
git_obj.file = git_obj.repo.toplevel .. util.path_sep .. git_obj.relpath
|
||||
bcache.file = git_obj.file
|
||||
git_obj:update_file_info()
|
||||
scheduler()
|
||||
|
||||
local bufexists = vim.fn.bufexists(bcache.file) == 1
|
||||
local old_name = api.nvim_buf_get_name(bufnr)
|
||||
local bufexists = vim.fn.bufexists(bcache.file) == 1
|
||||
local old_name = api.nvim_buf_get_name(bufnr)
|
||||
|
||||
if not bufexists then
|
||||
util.buf_rename(bufnr, bcache.file)
|
||||
end
|
||||
if not bufexists then
|
||||
util.buf_rename(bufnr, bcache.file)
|
||||
end
|
||||
|
||||
local msg = bufexists and 'Cannot rename' or 'Renamed'
|
||||
dprintf('%s buffer %d from %s to %s', msg, bufnr, old_name, bcache.file)
|
||||
end
|
||||
local msg = bufexists and 'Cannot rename' or 'Renamed'
|
||||
dprintf('%s buffer %d from %s to %s', msg, bufnr, old_name, bcache.file)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M.watch_gitdir(bufnr, gitdir)
|
||||
if not config.watch_gitdir.enable then
|
||||
return
|
||||
end
|
||||
if not config.watch_gitdir.enable then
|
||||
return
|
||||
end
|
||||
|
||||
dprintf('Watching git dir')
|
||||
local w = uv.new_fs_poll(true)
|
||||
w:start(gitdir, config.watch_gitdir.interval, void(function(err)
|
||||
dprintf('Watching git dir')
|
||||
local w = uv.new_fs_poll(true)
|
||||
w:start(
|
||||
gitdir,
|
||||
config.watch_gitdir.interval,
|
||||
void(function(err)
|
||||
local __FUNC__ = 'watcher_cb'
|
||||
if err then
|
||||
dprintf('Git dir update error: %s', err)
|
||||
return
|
||||
dprintf('Git dir update error: %s', err)
|
||||
return
|
||||
end
|
||||
dprint('Git dir update')
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
if not bcache then
|
||||
-- Very occasionally an external git operation may cause the buffer to
|
||||
-- detach and update the git dir simultaneously. When this happens this
|
||||
-- handler will trigger but there will be no cache.
|
||||
dprint('Has detached, aborting')
|
||||
return
|
||||
-- Very occasionally an external git operation may cause the buffer to
|
||||
-- detach and update the git dir simultaneously. When this happens this
|
||||
-- handler will trigger but there will be no cache.
|
||||
dprint('Has detached, aborting')
|
||||
return
|
||||
end
|
||||
|
||||
local git_obj = bcache.git_obj
|
||||
@ -521,62 +524,63 @@ function M.watch_gitdir(bufnr, gitdir)
|
||||
git_obj:update_file_info()
|
||||
|
||||
if config.watch_gitdir.follow_files and was_tracked and not git_obj.object_name then
|
||||
-- File was tracked but is no longer tracked. Must of been removed or
|
||||
-- moved. Check if it was moved and switch to it.
|
||||
handle_moved(bufnr, bcache, old_relpath)
|
||||
-- File was tracked but is no longer tracked. Must of been removed or
|
||||
-- moved. Check if it was moved and switch to it.
|
||||
handle_moved(bufnr, bcache, old_relpath)
|
||||
end
|
||||
|
||||
bcache:invalidate()
|
||||
|
||||
M.update(bufnr, bcache)
|
||||
end))
|
||||
return w
|
||||
end)
|
||||
)
|
||||
return w
|
||||
end
|
||||
|
||||
function M.reset_signs()
|
||||
-- Remove all signs
|
||||
if signs_normal then
|
||||
signs_normal:reset()
|
||||
end
|
||||
if signs_staged then
|
||||
signs_staged:reset()
|
||||
end
|
||||
-- Remove all signs
|
||||
if signs_normal then
|
||||
signs_normal:reset()
|
||||
end
|
||||
if signs_staged then
|
||||
signs_staged:reset()
|
||||
end
|
||||
end
|
||||
|
||||
local function on_win(_, _, bufnr, topline, botline_guess)
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache or not bcache.hunks then
|
||||
return false
|
||||
end
|
||||
local botline = math.min(botline_guess, api.nvim_buf_line_count(bufnr))
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache or not bcache.hunks then
|
||||
return false
|
||||
end
|
||||
local botline = math.min(botline_guess, api.nvim_buf_line_count(bufnr))
|
||||
|
||||
local untracked = bcache.git_obj.object_name == nil
|
||||
local untracked = bcache.git_obj.object_name == nil
|
||||
|
||||
apply_win_signs(bufnr, topline + 1, botline + 1, false, untracked)
|
||||
apply_win_signs(bufnr, topline + 1, botline + 1, false, untracked)
|
||||
|
||||
if not (config.word_diff and config.diff_opts.internal) then
|
||||
return false
|
||||
end
|
||||
if not (config.word_diff and config.diff_opts.internal) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function on_line(_, _, bufnr, row)
|
||||
apply_word_diff(bufnr, row)
|
||||
apply_word_diff(bufnr, row)
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
-- Calling this before any await calls will stop nvim's intro messages being
|
||||
-- displayed
|
||||
api.nvim_set_decoration_provider(ns, {
|
||||
on_win = on_win,
|
||||
on_line = on_line,
|
||||
})
|
||||
-- Calling this before any await calls will stop nvim's intro messages being
|
||||
-- displayed
|
||||
api.nvim_set_decoration_provider(ns, {
|
||||
on_win = on_win,
|
||||
on_line = on_line,
|
||||
})
|
||||
|
||||
signs_normal = Signs.new(config.signs)
|
||||
if config._signs_staged_enable then
|
||||
signs_staged = Signs.new(config._signs_staged, 'staged')
|
||||
end
|
||||
signs_normal = Signs.new(config.signs)
|
||||
if config._signs_staged_enable then
|
||||
signs_staged = Signs.new(config._signs_staged, 'staged')
|
||||
end
|
||||
|
||||
M.update_debounced = debounce_trailing(config.update_debounce, void(M.update))
|
||||
M.update_debounced = debounce_trailing(config.update_debounce, void(M.update))
|
||||
end
|
||||
|
||||
return M
|
||||
|
124
lua/gitsigns/mappings.lua
generated
124
lua/gitsigns/mappings.lua
generated
@ -5,83 +5,91 @@ local validate = vim.validate
|
||||
local api = vim.api
|
||||
|
||||
local valid_modes = {
|
||||
n = 'n', v = 'v', x = 'x', i = 'i', o = 'o', t = 't', c = 'c', s = 's',
|
||||
-- :map! and :map
|
||||
['!'] = '!', [' '] = '',
|
||||
n = 'n',
|
||||
v = 'v',
|
||||
x = 'x',
|
||||
i = 'i',
|
||||
o = 'o',
|
||||
t = 't',
|
||||
c = 'c',
|
||||
s = 's',
|
||||
-- :map! and :map
|
||||
['!'] = '!',
|
||||
[' '] = '',
|
||||
}
|
||||
|
||||
local valid_options = {
|
||||
buffer = 'boolean',
|
||||
expr = 'boolean',
|
||||
noremap = 'boolean',
|
||||
nowait = 'boolean',
|
||||
script = 'boolean',
|
||||
silent = 'boolean',
|
||||
unique = 'boolean',
|
||||
buffer = 'boolean',
|
||||
expr = 'boolean',
|
||||
noremap = 'boolean',
|
||||
nowait = 'boolean',
|
||||
script = 'boolean',
|
||||
silent = 'boolean',
|
||||
unique = 'boolean',
|
||||
}
|
||||
|
||||
local function validate_option_keywords(options)
|
||||
for option_name, expected_type in pairs(valid_options) do
|
||||
local value = options[option_name]
|
||||
if value then
|
||||
validate({
|
||||
[option_name] = { value, expected_type },
|
||||
})
|
||||
end
|
||||
end
|
||||
for option_name, expected_type in pairs(valid_options) do
|
||||
local value = options[option_name]
|
||||
if value then
|
||||
validate({
|
||||
[option_name] = { value, expected_type },
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_mappings(mappings, bufnr)
|
||||
validate({
|
||||
mappings = { mappings, 'table' },
|
||||
})
|
||||
validate({
|
||||
mappings = { mappings, 'table' },
|
||||
})
|
||||
|
||||
local default_options = {}
|
||||
for key, val in pairs(mappings) do
|
||||
local default_options = {}
|
||||
for key, val in pairs(mappings) do
|
||||
-- Skip any inline default keywords.
|
||||
if valid_options[key] then
|
||||
default_options[key] = val
|
||||
end
|
||||
end
|
||||
|
||||
for key, opts in pairs(mappings) do
|
||||
repeat
|
||||
-- Skip any inline default keywords.
|
||||
if valid_options[key] then
|
||||
default_options[key] = val
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for key, opts in pairs(mappings) do
|
||||
repeat
|
||||
-- Skip any inline default keywords.
|
||||
if valid_options[key] then
|
||||
break
|
||||
end
|
||||
local rhs
|
||||
local options
|
||||
if type(opts) == 'string' then
|
||||
rhs = opts
|
||||
options = {}
|
||||
elseif type(opts) == 'table' then
|
||||
rhs = opts[1]
|
||||
local boptions = {}
|
||||
for k in pairs(valid_options) do
|
||||
boptions[k] = opts[k]
|
||||
end
|
||||
options = boptions
|
||||
else
|
||||
error(('Invalid type for option rhs: %q = %s'):format(type(opts), vim.inspect(opts)))
|
||||
end
|
||||
options = vim.tbl_extend('keep', default_options, options)
|
||||
|
||||
local rhs
|
||||
local options
|
||||
if type(opts) == "string" then
|
||||
rhs = opts
|
||||
options = {}
|
||||
elseif type(opts) == "table" then
|
||||
rhs = opts[1]
|
||||
local boptions = {}
|
||||
for k in pairs(valid_options) do
|
||||
boptions[k] = opts[k]
|
||||
end
|
||||
options = boptions
|
||||
else
|
||||
error(("Invalid type for option rhs: %q = %s"):format(type(opts), vim.inspect(opts)))
|
||||
end
|
||||
options = vim.tbl_extend('keep', default_options, options)
|
||||
validate_option_keywords(options)
|
||||
|
||||
validate_option_keywords(options)
|
||||
local mode, mapping = key:match('^(.)[ ]*(.+)$')
|
||||
|
||||
local mode, mapping = key:match("^(.)[ ]*(.+)$")
|
||||
if not mode or not valid_modes[mode] then
|
||||
error('Invalid mode specified for keymapping. mode=' .. mode)
|
||||
end
|
||||
|
||||
if not mode or not valid_modes[mode] then
|
||||
error("Invalid mode specified for keymapping. mode=" .. mode)
|
||||
end
|
||||
-- In case users haven't updated their config.
|
||||
options.buffer = nil
|
||||
|
||||
-- In case users haven't updated their config.
|
||||
options.buffer = nil
|
||||
|
||||
api.nvim_buf_set_keymap(bufnr, mode, mapping, rhs, options)
|
||||
until true
|
||||
end
|
||||
api.nvim_buf_set_keymap(bufnr, mode, mapping, rhs, options)
|
||||
until true
|
||||
end
|
||||
end
|
||||
|
||||
return apply_mappings
|
||||
|
9
lua/gitsigns/message.lua
generated
9
lua/gitsigns/message.lua
generated
@ -1,16 +1,11 @@
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
M.warn = vim.schedule_wrap(function(s, ...)
|
||||
vim.notify(s:format(...), vim.log.levels.WARN, { title = 'gitsigns' })
|
||||
vim.notify(s:format(...), vim.log.levels.WARN, { title = 'gitsigns' })
|
||||
end)
|
||||
|
||||
M.error = vim.schedule_wrap(function(s, ...)
|
||||
vim.notify(s:format(...), vim.log.levels.ERROR, { title = 'gitsigns' })
|
||||
vim.notify(s:format(...), vim.log.levels.ERROR, { title = 'gitsigns' })
|
||||
end)
|
||||
|
||||
return M
|
||||
|
358
lua/gitsigns/popup.lua
generated
358
lua/gitsigns/popup.lua
generated
@ -1,245 +1,237 @@
|
||||
local popup = {HlMark = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local popup = { HlMark = {} }
|
||||
|
||||
local HlMark = popup.HlMark
|
||||
|
||||
local api = vim.api
|
||||
|
||||
local function bufnr_calc_width(bufnr, lines)
|
||||
return api.nvim_buf_call(bufnr, function()
|
||||
local width = 0
|
||||
for _, l in ipairs(lines) do
|
||||
if vim.fn.type(l) == vim.v.t_string then
|
||||
local len = vim.fn.strdisplaywidth(l)
|
||||
if len > width then
|
||||
width = len
|
||||
end
|
||||
end
|
||||
return api.nvim_buf_call(bufnr, function()
|
||||
local width = 0
|
||||
for _, l in ipairs(lines) do
|
||||
if vim.fn.type(l) == vim.v.t_string then
|
||||
local len = vim.fn.strdisplaywidth(l)
|
||||
if len > width then
|
||||
width = len
|
||||
end
|
||||
end
|
||||
return width + 1 -- Add 1 for some miinor padding
|
||||
end)
|
||||
end
|
||||
return width + 1 -- Add 1 for some miinor padding
|
||||
end)
|
||||
end
|
||||
|
||||
-- Expand height until all lines are visible to account for wrapped lines.
|
||||
local function expand_height(winid, nlines)
|
||||
local newheight = 0
|
||||
for _ = 0, 50 do
|
||||
local winheight = api.nvim_win_get_height(winid)
|
||||
if newheight > winheight then
|
||||
-- Window must be max height
|
||||
break
|
||||
end
|
||||
local wd = api.nvim_win_call(winid, function()
|
||||
return vim.fn.line('w$')
|
||||
end)
|
||||
if wd >= nlines then
|
||||
break
|
||||
end
|
||||
newheight = winheight + nlines - wd
|
||||
api.nvim_win_set_height(winid, newheight)
|
||||
end
|
||||
local newheight = 0
|
||||
for _ = 0, 50 do
|
||||
local winheight = api.nvim_win_get_height(winid)
|
||||
if newheight > winheight then
|
||||
-- Window must be max height
|
||||
break
|
||||
end
|
||||
local wd = api.nvim_win_call(winid, function()
|
||||
return vim.fn.line('w$')
|
||||
end)
|
||||
if wd >= nlines then
|
||||
break
|
||||
end
|
||||
newheight = winheight + nlines - wd
|
||||
api.nvim_win_set_height(winid, newheight)
|
||||
end
|
||||
end
|
||||
|
||||
local function offset_hlmarks(hlmarks, row_offset)
|
||||
for _, h in ipairs(hlmarks) do
|
||||
if h.start_row then
|
||||
h.start_row = h.start_row + row_offset
|
||||
end
|
||||
if h.end_row then
|
||||
h.end_row = h.end_row + row_offset
|
||||
end
|
||||
end
|
||||
for _, h in ipairs(hlmarks) do
|
||||
if h.start_row then
|
||||
h.start_row = h.start_row + row_offset
|
||||
end
|
||||
if h.end_row then
|
||||
h.end_row = h.end_row + row_offset
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function process_linesspec(fmt)
|
||||
local lines = {}
|
||||
local hls = {}
|
||||
local lines = {}
|
||||
local hls = {}
|
||||
|
||||
local row = 0
|
||||
for _, section in ipairs(fmt) do
|
||||
local sec = {}
|
||||
local pos = 0
|
||||
for _, part in ipairs(section) do
|
||||
local text = part[1]
|
||||
local hl = part[2]
|
||||
local row = 0
|
||||
for _, section in ipairs(fmt) do
|
||||
local sec = {}
|
||||
local pos = 0
|
||||
for _, part in ipairs(section) do
|
||||
local text = part[1]
|
||||
local hl = part[2]
|
||||
|
||||
sec[#sec + 1] = text
|
||||
sec[#sec + 1] = text
|
||||
|
||||
local srow = row
|
||||
local scol = pos
|
||||
local srow = row
|
||||
local scol = pos
|
||||
|
||||
local ts = vim.split(text, '\n')
|
||||
local ts = vim.split(text, '\n')
|
||||
|
||||
if #ts > 1 then
|
||||
pos = 0
|
||||
row = row + #ts - 1
|
||||
else
|
||||
pos = pos + #text
|
||||
end
|
||||
|
||||
if type(hl) == "string" then
|
||||
hls[#hls + 1] = {
|
||||
hl_group = hl,
|
||||
start_row = srow,
|
||||
end_row = row,
|
||||
start_col = scol,
|
||||
end_col = pos,
|
||||
}
|
||||
else -- hl is {HlMark}
|
||||
offset_hlmarks(hl, srow)
|
||||
vim.list_extend(hls, hl)
|
||||
end
|
||||
if #ts > 1 then
|
||||
pos = 0
|
||||
row = row + #ts - 1
|
||||
else
|
||||
pos = pos + #text
|
||||
end
|
||||
for _, l in ipairs(vim.split(table.concat(sec, ''), '\n')) do
|
||||
lines[#lines + 1] = l
|
||||
end
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
return lines, hls
|
||||
if type(hl) == 'string' then
|
||||
hls[#hls + 1] = {
|
||||
hl_group = hl,
|
||||
start_row = srow,
|
||||
end_row = row,
|
||||
start_col = scol,
|
||||
end_col = pos,
|
||||
}
|
||||
else -- hl is {HlMark}
|
||||
offset_hlmarks(hl, srow)
|
||||
vim.list_extend(hls, hl)
|
||||
end
|
||||
end
|
||||
for _, l in ipairs(vim.split(table.concat(sec, ''), '\n')) do
|
||||
lines[#lines + 1] = l
|
||||
end
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
return lines, hls
|
||||
end
|
||||
|
||||
local function close_all_but(id)
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview ~= nil and
|
||||
vim.w[winid].gitsigns_preview ~= id then
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
end
|
||||
end
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview ~= nil and vim.w[winid].gitsigns_preview ~= id then
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function popup.close(id)
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview == id then
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
end
|
||||
end
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview == id then
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function popup.create0(lines, opts, id)
|
||||
-- Close any popups not matching id
|
||||
close_all_but(id)
|
||||
-- Close any popups not matching id
|
||||
close_all_but(id)
|
||||
|
||||
local ts = vim.bo.tabstop
|
||||
local bufnr = api.nvim_create_buf(false, true)
|
||||
assert(bufnr, "Failed to create buffer")
|
||||
local ts = vim.bo.tabstop
|
||||
local bufnr = api.nvim_create_buf(false, true)
|
||||
assert(bufnr, 'Failed to create buffer')
|
||||
|
||||
-- In case nvim was opened with '-M'
|
||||
vim.bo[bufnr].modifiable = true
|
||||
api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||
vim.bo[bufnr].modifiable = false
|
||||
-- In case nvim was opened with '-M'
|
||||
vim.bo[bufnr].modifiable = true
|
||||
api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||
vim.bo[bufnr].modifiable = false
|
||||
|
||||
-- Set tabstop before calculating the buffer width so that the correct width
|
||||
-- is calculated
|
||||
vim.bo[bufnr].tabstop = ts
|
||||
-- Set tabstop before calculating the buffer width so that the correct width
|
||||
-- is calculated
|
||||
vim.bo[bufnr].tabstop = ts
|
||||
|
||||
local opts1 = vim.deepcopy(opts or {})
|
||||
opts1.height = opts1.height or #lines -- Guess, adjust later
|
||||
opts1.width = opts1.width or bufnr_calc_width(bufnr, lines)
|
||||
local opts1 = vim.deepcopy(opts or {})
|
||||
opts1.height = opts1.height or #lines -- Guess, adjust later
|
||||
opts1.width = opts1.width or bufnr_calc_width(bufnr, lines)
|
||||
|
||||
local winid = api.nvim_open_win(bufnr, false, opts1)
|
||||
local winid = api.nvim_open_win(bufnr, false, opts1)
|
||||
|
||||
vim.w[winid].gitsigns_preview = id or true
|
||||
vim.w[winid].gitsigns_preview = id or true
|
||||
|
||||
if not opts.height then
|
||||
expand_height(winid, #lines)
|
||||
end
|
||||
if not opts.height then
|
||||
expand_height(winid, #lines)
|
||||
end
|
||||
|
||||
if opts1.style == 'minimal' then
|
||||
-- If 'signcolumn' = auto:1-2, then a empty signcolumn will appear and cause
|
||||
-- line wrapping.
|
||||
vim.wo[winid].signcolumn = 'no'
|
||||
end
|
||||
if opts1.style == 'minimal' then
|
||||
-- If 'signcolumn' = auto:1-2, then a empty signcolumn will appear and cause
|
||||
-- line wrapping.
|
||||
vim.wo[winid].signcolumn = 'no'
|
||||
end
|
||||
|
||||
-- Close the popup when navigating to any window which is not the preview
|
||||
-- itself.
|
||||
local group = 'gitsigns_popup'
|
||||
local group_id = api.nvim_create_augroup(group, {})
|
||||
local old_cursor = api.nvim_win_get_cursor(0)
|
||||
-- Close the popup when navigating to any window which is not the preview
|
||||
-- itself.
|
||||
local group = 'gitsigns_popup'
|
||||
local group_id = api.nvim_create_augroup(group, {})
|
||||
local old_cursor = api.nvim_win_get_cursor(0)
|
||||
|
||||
api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
|
||||
group = group_id,
|
||||
callback = function()
|
||||
local cursor = api.nvim_win_get_cursor(0)
|
||||
-- Did the cursor REALLY change (neovim/neovim#12923)
|
||||
if (old_cursor[1] ~= cursor[1] or old_cursor[2] ~= cursor[2]) and
|
||||
api.nvim_get_current_win() ~= winid then
|
||||
-- Clear the augroup
|
||||
api.nvim_create_augroup(group, {})
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
return
|
||||
end
|
||||
old_cursor = cursor
|
||||
end,
|
||||
})
|
||||
api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
|
||||
group = group_id,
|
||||
callback = function()
|
||||
local cursor = api.nvim_win_get_cursor(0)
|
||||
-- Did the cursor REALLY change (neovim/neovim#12923)
|
||||
if
|
||||
(old_cursor[1] ~= cursor[1] or old_cursor[2] ~= cursor[2])
|
||||
and api.nvim_get_current_win() ~= winid
|
||||
then
|
||||
-- Clear the augroup
|
||||
api.nvim_create_augroup(group, {})
|
||||
pcall(api.nvim_win_close, winid, true)
|
||||
return
|
||||
end
|
||||
old_cursor = cursor
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('WinClosed', {
|
||||
pattern = tostring(winid),
|
||||
group = group_id,
|
||||
callback = function()
|
||||
-- Clear the augroup
|
||||
api.nvim_create_augroup(group, {})
|
||||
end,
|
||||
})
|
||||
api.nvim_create_autocmd('WinClosed', {
|
||||
pattern = tostring(winid),
|
||||
group = group_id,
|
||||
callback = function()
|
||||
-- Clear the augroup
|
||||
api.nvim_create_augroup(group, {})
|
||||
end,
|
||||
})
|
||||
|
||||
-- update window position to follow the cursor when scrolling
|
||||
api.nvim_create_autocmd('WinScrolled', {
|
||||
buffer = api.nvim_get_current_buf(),
|
||||
group = group_id,
|
||||
callback = function()
|
||||
if api.nvim_win_is_valid(winid) then
|
||||
api.nvim_win_set_config(winid, opts1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
-- update window position to follow the cursor when scrolling
|
||||
api.nvim_create_autocmd('WinScrolled', {
|
||||
buffer = api.nvim_get_current_buf(),
|
||||
group = group_id,
|
||||
callback = function()
|
||||
if api.nvim_win_is_valid(winid) then
|
||||
api.nvim_win_set_config(winid, opts1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return winid, bufnr
|
||||
return winid, bufnr
|
||||
end
|
||||
|
||||
local ns = api.nvim_create_namespace('gitsigns_popup')
|
||||
|
||||
function popup.create(lines_spec, opts, id)
|
||||
local lines, highlights = process_linesspec(lines_spec)
|
||||
local winid, bufnr = popup.create0(lines, opts, id)
|
||||
local lines, highlights = process_linesspec(lines_spec)
|
||||
local winid, bufnr = popup.create0(lines, opts, id)
|
||||
|
||||
for _, hl in ipairs(highlights) do
|
||||
local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, ns, hl.start_row, hl.start_col or 0, {
|
||||
hl_group = hl.hl_group,
|
||||
end_row = hl.end_row,
|
||||
end_col = hl.end_col,
|
||||
hl_eol = true,
|
||||
})
|
||||
if not ok then
|
||||
error(vim.inspect(hl) .. '\n' .. err)
|
||||
end
|
||||
end
|
||||
for _, hl in ipairs(highlights) do
|
||||
local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, ns, hl.start_row, hl.start_col or 0, {
|
||||
hl_group = hl.hl_group,
|
||||
end_row = hl.end_row,
|
||||
end_col = hl.end_col,
|
||||
hl_eol = true,
|
||||
})
|
||||
if not ok then
|
||||
error(vim.inspect(hl) .. '\n' .. err)
|
||||
end
|
||||
end
|
||||
|
||||
return winid, bufnr
|
||||
return winid, bufnr
|
||||
end
|
||||
|
||||
function popup.is_open(id)
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview == id then
|
||||
return winid
|
||||
end
|
||||
end
|
||||
return nil
|
||||
for _, winid in ipairs(api.nvim_list_wins()) do
|
||||
if vim.w[winid].gitsigns_preview == id then
|
||||
return winid
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function popup.focus_open(id)
|
||||
local winid = popup.is_open(id)
|
||||
if winid then
|
||||
api.nvim_set_current_win(winid)
|
||||
end
|
||||
return winid
|
||||
local winid = popup.is_open(id)
|
||||
if winid then
|
||||
api.nvim_set_current_win(winid)
|
||||
end
|
||||
return winid
|
||||
end
|
||||
|
||||
return popup
|
||||
|
34
lua/gitsigns/repeat.lua
generated
34
lua/gitsigns/repeat.lua
generated
@ -2,27 +2,27 @@ local api = vim.api
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
function M.mk_repeatable(fn)
|
||||
return function(...)
|
||||
local args = { ... }
|
||||
local nargs = select('#', ...)
|
||||
vim.go.operatorfunc = "v:lua.require'gitsigns.repeat'.repeat_action"
|
||||
return function(...)
|
||||
local args = { ... }
|
||||
local nargs = select('#', ...)
|
||||
vim.go.operatorfunc = "v:lua.require'gitsigns.repeat'.repeat_action"
|
||||
|
||||
M.repeat_action = function()
|
||||
fn(unpack(args, 1, nargs))
|
||||
if vim.fn.exists('*repeat#set') == 1 then
|
||||
local action = api.nvim_replace_termcodes(
|
||||
string.format('<cmd>call %s()<cr>', vim.go.operatorfunc),
|
||||
true, true, true)
|
||||
vim.fn['repeat#set'](action, -1)
|
||||
end
|
||||
M.repeat_action = function()
|
||||
fn(unpack(args, 1, nargs))
|
||||
if vim.fn.exists('*repeat#set') == 1 then
|
||||
local action = api.nvim_replace_termcodes(
|
||||
string.format('<cmd>call %s()<cr>', vim.go.operatorfunc),
|
||||
true,
|
||||
true,
|
||||
true
|
||||
)
|
||||
vim.fn['repeat#set'](action, -1)
|
||||
end
|
||||
end
|
||||
|
||||
vim.cmd('normal! g@l')
|
||||
end
|
||||
vim.cmd('normal! g@l')
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
55
lua/gitsigns/signs.lua
generated
55
lua/gitsigns/signs.lua
generated
@ -1,5 +1,4 @@
|
||||
local config = require('gitsigns.config').config
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
|
||||
local dprint = require('gitsigns.debug.log').dprint
|
||||
|
||||
@ -10,34 +9,34 @@ local B = require('gitsigns.signs.base')
|
||||
-- end
|
||||
|
||||
function B.new(cfg, name)
|
||||
local __FUNC__ = 'signs.init'
|
||||
local C
|
||||
if config._extmark_signs then
|
||||
dprint('Using extmark signs')
|
||||
C = require('gitsigns.signs.extmarks')
|
||||
else
|
||||
dprint('Using vimfn signs')
|
||||
C = require('gitsigns.signs.vimfn')
|
||||
end
|
||||
local __FUNC__ = 'signs.init'
|
||||
local C
|
||||
if config._extmark_signs then
|
||||
dprint('Using extmark signs')
|
||||
C = require('gitsigns.signs.extmarks')
|
||||
else
|
||||
dprint('Using vimfn signs')
|
||||
C = require('gitsigns.signs.vimfn')
|
||||
end
|
||||
|
||||
local hls = (name == 'staged' and config._signs_staged or config.signs)
|
||||
-- Add when config.signs.*.[hl,numhl,linehl] are removed
|
||||
-- for _, t in ipairs {
|
||||
-- 'add',
|
||||
-- 'change',
|
||||
-- 'delete',
|
||||
-- 'topdelete',
|
||||
-- 'changedelete',
|
||||
-- 'untracked',
|
||||
-- } do
|
||||
-- local hl = string.format('GitSigns%s%s', name, capitalise_word(t))
|
||||
-- obj.hls[t] = {
|
||||
-- hl = hl,
|
||||
-- numhl = hl..'Nr',
|
||||
-- linehl = hl..'Ln',
|
||||
-- }
|
||||
-- end
|
||||
return C._new(cfg, hls, name)
|
||||
local hls = (name == 'staged' and config._signs_staged or config.signs)
|
||||
-- Add when config.signs.*.[hl,numhl,linehl] are removed
|
||||
-- for _, t in ipairs {
|
||||
-- 'add',
|
||||
-- 'change',
|
||||
-- 'delete',
|
||||
-- 'topdelete',
|
||||
-- 'changedelete',
|
||||
-- 'untracked',
|
||||
-- } do
|
||||
-- local hl = string.format('GitSigns%s%s', name, capitalise_word(t))
|
||||
-- obj.hls[t] = {
|
||||
-- hl = hl,
|
||||
-- numhl = hl..'Nr',
|
||||
-- linehl = hl..'Ln',
|
||||
-- }
|
||||
-- end
|
||||
return C._new(cfg, hls, name)
|
||||
end
|
||||
|
||||
return B
|
||||
|
43
lua/gitsigns/signs/base.lua
generated
43
lua/gitsigns/signs/base.lua
generated
@ -1,46 +1,9 @@
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
|
||||
local M = {Sign = {}, HlDef = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Used by signs/extmarks.tl
|
||||
|
||||
|
||||
-- Used by signs/vimfn.tl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = { Sign = {}, HlDef = {} }
|
||||
|
||||
-- Used by signs/extmarks.tl
|
||||
|
||||
-- Used by signs/vimfn.tl
|
||||
|
||||
return M
|
||||
|
119
lua/gitsigns/signs/extmarks.lua
generated
119
lua/gitsigns/signs/extmarks.lua
generated
@ -10,80 +10,85 @@ local M = {}
|
||||
local group_base = 'gitsigns_extmark_signs_'
|
||||
|
||||
function M._new(cfg, hls, name)
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.config = cfg
|
||||
self.hls = hls
|
||||
self.group = group_base .. (name or '')
|
||||
self.ns = api.nvim_create_namespace(self.group)
|
||||
return self
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.config = cfg
|
||||
self.hls = hls
|
||||
self.group = group_base .. (name or '')
|
||||
self.ns = api.nvim_create_namespace(self.group)
|
||||
return self
|
||||
end
|
||||
|
||||
function M:on_lines(buf, _, last_orig, last_new)
|
||||
-- Remove extmarks on line deletions to mimic
|
||||
-- the behaviour of vim signs.
|
||||
if last_orig > last_new then
|
||||
self:remove(buf, last_new + 1, last_orig)
|
||||
end
|
||||
-- Remove extmarks on line deletions to mimic
|
||||
-- the behaviour of vim signs.
|
||||
if last_orig > last_new then
|
||||
self:remove(buf, last_new + 1, last_orig)
|
||||
end
|
||||
end
|
||||
|
||||
function M:remove(bufnr, start_lnum, end_lnum)
|
||||
if start_lnum then
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, start_lnum - 1, end_lnum or start_lnum)
|
||||
else
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, 0, -1)
|
||||
end
|
||||
if start_lnum then
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, start_lnum - 1, end_lnum or start_lnum)
|
||||
else
|
||||
api.nvim_buf_clear_namespace(bufnr, self.ns, 0, -1)
|
||||
end
|
||||
end
|
||||
|
||||
function M:add(bufnr, signs)
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
end
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
end
|
||||
|
||||
for _, s in ipairs(signs) do
|
||||
if not self:contains(bufnr, s.lnum) then
|
||||
local cs = self.config[s.type]
|
||||
local text = cs.text
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
local count = s.count
|
||||
local cc = config.count_chars
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
text = cs.text .. count_char
|
||||
end
|
||||
|
||||
local hls = self.hls[s.type]
|
||||
|
||||
local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, self.ns, s.lnum - 1, -1, {
|
||||
id = s.lnum,
|
||||
sign_text = config.signcolumn and text or '',
|
||||
priority = config.sign_priority,
|
||||
sign_hl_group = hls.hl,
|
||||
number_hl_group = config.numhl and hls.numhl or nil,
|
||||
line_hl_group = config.linehl and hls.linehl or nil,
|
||||
})
|
||||
|
||||
if not ok and config.debug_mode then
|
||||
vim.schedule(function()
|
||||
error(table.concat({
|
||||
string.format('Error placing extmark on line %d', s.lnum),
|
||||
err,
|
||||
}, '\n'))
|
||||
end)
|
||||
end
|
||||
for _, s in ipairs(signs) do
|
||||
if not self:contains(bufnr, s.lnum) then
|
||||
local cs = self.config[s.type]
|
||||
local text = cs.text
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
local count = s.count
|
||||
local cc = config.count_chars
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
text = cs.text .. count_char
|
||||
end
|
||||
end
|
||||
|
||||
local hls = self.hls[s.type]
|
||||
|
||||
local ok, err = pcall(api.nvim_buf_set_extmark, bufnr, self.ns, s.lnum - 1, -1, {
|
||||
id = s.lnum,
|
||||
sign_text = config.signcolumn and text or '',
|
||||
priority = config.sign_priority,
|
||||
sign_hl_group = hls.hl,
|
||||
number_hl_group = config.numhl and hls.numhl or nil,
|
||||
line_hl_group = config.linehl and hls.linehl or nil,
|
||||
})
|
||||
|
||||
if not ok and config.debug_mode then
|
||||
vim.schedule(function()
|
||||
error(table.concat({
|
||||
string.format('Error placing extmark on line %d', s.lnum),
|
||||
err,
|
||||
}, '\n'))
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:contains(bufnr, start, last)
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr, self.ns, { start - 1, 0 }, { last or start, 0 }, { limit = 1 })
|
||||
return #marks > 0
|
||||
local marks = api.nvim_buf_get_extmarks(
|
||||
bufnr,
|
||||
self.ns,
|
||||
{ start - 1, 0 },
|
||||
{ last or start, 0 },
|
||||
{ limit = 1 }
|
||||
)
|
||||
return #marks > 0
|
||||
end
|
||||
|
||||
function M:reset()
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
self:remove(buf)
|
||||
end
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
self:remove(buf)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
195
lua/gitsigns/signs/vimfn.lua
generated
195
lua/gitsigns/signs/vimfn.lua
generated
@ -18,146 +18,145 @@ local M = {}
|
||||
-- - skip adding a sign if it has already been placed.
|
||||
|
||||
local function capitalise_word(x)
|
||||
return x:sub(1, 1):upper() .. x:sub(2)
|
||||
return x:sub(1, 1):upper() .. x:sub(2)
|
||||
end
|
||||
|
||||
local sign_define_cache = {}
|
||||
local sign_name_cache = {}
|
||||
|
||||
local function get_sign_name(name, stype)
|
||||
local key = name .. stype
|
||||
if not sign_name_cache[key] then
|
||||
sign_name_cache[key] = string.format(
|
||||
'%s%s%s', 'GitSigns', capitalise_word(key), capitalise_word(stype))
|
||||
end
|
||||
local key = name .. stype
|
||||
if not sign_name_cache[key] then
|
||||
sign_name_cache[key] =
|
||||
string.format('%s%s%s', 'GitSigns', capitalise_word(key), capitalise_word(stype))
|
||||
end
|
||||
|
||||
return sign_name_cache[key]
|
||||
return sign_name_cache[key]
|
||||
end
|
||||
|
||||
local function sign_get(name)
|
||||
if not sign_define_cache[name] then
|
||||
local s = fn.sign_getdefined(name)
|
||||
if not vim.tbl_isempty(s) then
|
||||
sign_define_cache[name] = s
|
||||
end
|
||||
end
|
||||
return sign_define_cache[name]
|
||||
if not sign_define_cache[name] then
|
||||
local s = fn.sign_getdefined(name)
|
||||
if not vim.tbl_isempty(s) then
|
||||
sign_define_cache[name] = s
|
||||
end
|
||||
end
|
||||
return sign_define_cache[name]
|
||||
end
|
||||
|
||||
local function define_sign(name, opts, redefine)
|
||||
if redefine then
|
||||
sign_define_cache[name] = nil
|
||||
fn.sign_undefine(name)
|
||||
fn.sign_define(name, opts)
|
||||
elseif not sign_get(name) then
|
||||
fn.sign_define(name, opts)
|
||||
end
|
||||
if redefine then
|
||||
sign_define_cache[name] = nil
|
||||
fn.sign_undefine(name)
|
||||
fn.sign_define(name, opts)
|
||||
elseif not sign_get(name) then
|
||||
fn.sign_define(name, opts)
|
||||
end
|
||||
end
|
||||
|
||||
local function define_signs(obj, redefine)
|
||||
-- Define signs
|
||||
for stype, cs in pairs(obj.config) do
|
||||
local hls = obj.hls[stype]
|
||||
define_sign(get_sign_name(obj.name, stype), {
|
||||
texthl = hls.hl,
|
||||
text = config.signcolumn and cs.text or nil,
|
||||
numhl = config.numhl and hls.numhl or nil,
|
||||
linehl = config.linehl and hls.linehl or nil,
|
||||
}, redefine)
|
||||
end
|
||||
-- Define signs
|
||||
for stype, cs in pairs(obj.config) do
|
||||
local hls = obj.hls[stype]
|
||||
define_sign(get_sign_name(obj.name, stype), {
|
||||
texthl = hls.hl,
|
||||
text = config.signcolumn and cs.text or nil,
|
||||
numhl = config.numhl and hls.numhl or nil,
|
||||
linehl = config.linehl and hls.linehl or nil,
|
||||
}, redefine)
|
||||
end
|
||||
end
|
||||
|
||||
local group_base = 'gitsigns_vimfn_signs_'
|
||||
|
||||
function M._new(cfg, hls, name)
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.name = name or ''
|
||||
self.group = group_base .. (name or '')
|
||||
self.config = cfg
|
||||
self.hls = hls
|
||||
self.placed = emptytable()
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.name = name or ''
|
||||
self.group = group_base .. (name or '')
|
||||
self.config = cfg
|
||||
self.hls = hls
|
||||
self.placed = emptytable()
|
||||
|
||||
define_signs(self, false)
|
||||
define_signs(self, false)
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
function M:on_lines(_, _, _, _)
|
||||
end
|
||||
function M:on_lines(_, _, _, _) end
|
||||
|
||||
function M:remove(bufnr, start_lnum, end_lnum)
|
||||
end_lnum = end_lnum or start_lnum
|
||||
end_lnum = end_lnum or start_lnum
|
||||
|
||||
if start_lnum then
|
||||
for lnum = start_lnum, end_lnum do
|
||||
self.placed[bufnr][lnum] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr, id = lnum })
|
||||
end
|
||||
else
|
||||
self.placed[bufnr] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr })
|
||||
end
|
||||
if start_lnum then
|
||||
for lnum = start_lnum, end_lnum do
|
||||
self.placed[bufnr][lnum] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr, id = lnum })
|
||||
end
|
||||
else
|
||||
self.placed[bufnr] = nil
|
||||
fn.sign_unplace(self.group, { buffer = bufnr })
|
||||
end
|
||||
end
|
||||
|
||||
function M:add(bufnr, signs)
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
end
|
||||
if not config.signcolumn and not config.numhl and not config.linehl then
|
||||
-- Don't place signs if it won't show anything
|
||||
return
|
||||
end
|
||||
|
||||
local to_place = {}
|
||||
local to_place = {}
|
||||
|
||||
for _, s in ipairs(signs) do
|
||||
local sign_name = get_sign_name(self.name, s.type)
|
||||
for _, s in ipairs(signs) do
|
||||
local sign_name = get_sign_name(self.name, s.type)
|
||||
|
||||
local cs = self.config[s.type]
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
local count = s.count
|
||||
local cc = config.count_chars
|
||||
local count_suffix = cc[count] and tostring(count) or (cc['+'] and 'Plus') or ''
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
local hls = self.hls[s.type]
|
||||
sign_name = sign_name .. count_suffix
|
||||
define_sign(sign_name, {
|
||||
texthl = hls.hl,
|
||||
text = config.signcolumn and cs.text .. count_char or '',
|
||||
numhl = config.numhl and hls.numhl or nil,
|
||||
linehl = config.linehl and hls.linehl or nil,
|
||||
})
|
||||
end
|
||||
local cs = self.config[s.type]
|
||||
if config.signcolumn and cs.show_count and s.count then
|
||||
local count = s.count
|
||||
local cc = config.count_chars
|
||||
local count_suffix = cc[count] and tostring(count) or (cc['+'] and 'Plus') or ''
|
||||
local count_char = cc[count] or cc['+'] or ''
|
||||
local hls = self.hls[s.type]
|
||||
sign_name = sign_name .. count_suffix
|
||||
define_sign(sign_name, {
|
||||
texthl = hls.hl,
|
||||
text = config.signcolumn and cs.text .. count_char or '',
|
||||
numhl = config.numhl and hls.numhl or nil,
|
||||
linehl = config.linehl and hls.linehl or nil,
|
||||
})
|
||||
end
|
||||
|
||||
if not self.placed[bufnr][s.lnum] then
|
||||
local sign = {
|
||||
id = s.lnum,
|
||||
group = self.group,
|
||||
name = sign_name,
|
||||
buffer = bufnr,
|
||||
lnum = s.lnum,
|
||||
priority = config.sign_priority,
|
||||
}
|
||||
self.placed[bufnr][s.lnum] = s
|
||||
to_place[#to_place + 1] = sign
|
||||
end
|
||||
end
|
||||
if not self.placed[bufnr][s.lnum] then
|
||||
local sign = {
|
||||
id = s.lnum,
|
||||
group = self.group,
|
||||
name = sign_name,
|
||||
buffer = bufnr,
|
||||
lnum = s.lnum,
|
||||
priority = config.sign_priority,
|
||||
}
|
||||
self.placed[bufnr][s.lnum] = s
|
||||
to_place[#to_place + 1] = sign
|
||||
end
|
||||
end
|
||||
|
||||
if #to_place > 0 then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
if #to_place > 0 then
|
||||
fn.sign_placelist(to_place)
|
||||
end
|
||||
end
|
||||
|
||||
function M:contains(bufnr, start, last)
|
||||
for i = start + 1, last + 1 do
|
||||
if self.placed[bufnr][i] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
for i = start + 1, last + 1 do
|
||||
if self.placed[bufnr][i] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function M:reset()
|
||||
self.placed = emptytable()
|
||||
fn.sign_unplace(self.group)
|
||||
define_signs(self, true)
|
||||
self.placed = emptytable()
|
||||
fn.sign_unplace(self.group)
|
||||
define_signs(self, true)
|
||||
end
|
||||
|
||||
return M
|
||||
|
46
lua/gitsigns/status.lua
generated
46
lua/gitsigns/status.lua
generated
@ -1,46 +1,38 @@
|
||||
local api = vim.api
|
||||
|
||||
|
||||
local StatusObj = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local Status = {
|
||||
StatusObj = StatusObj,
|
||||
StatusObj = StatusObj,
|
||||
}
|
||||
|
||||
function Status:update(bufnr, status)
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
local bstatus = vim.b[bufnr].gitsigns_status_dict
|
||||
if bstatus then
|
||||
status = vim.tbl_extend('force', bstatus, status)
|
||||
end
|
||||
vim.b[bufnr].gitsigns_head = status.head or ''
|
||||
vim.b[bufnr].gitsigns_status_dict = status
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
local bstatus = vim.b[bufnr].gitsigns_status_dict
|
||||
if bstatus then
|
||||
status = vim.tbl_extend('force', bstatus, status)
|
||||
end
|
||||
vim.b[bufnr].gitsigns_head = status.head or ''
|
||||
vim.b[bufnr].gitsigns_status_dict = status
|
||||
|
||||
local config = require('gitsigns.config').config
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
vim.b[bufnr].gitsigns_status = config.status_formatter(status)
|
||||
vim.b[bufnr].gitsigns_status = config.status_formatter(status)
|
||||
end
|
||||
|
||||
function Status:clear(bufnr)
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
vim.b[bufnr].gitsigns_head = nil
|
||||
vim.b[bufnr].gitsigns_status_dict = nil
|
||||
vim.b[bufnr].gitsigns_status = nil
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
vim.b[bufnr].gitsigns_head = nil
|
||||
vim.b[bufnr].gitsigns_status_dict = nil
|
||||
vim.b[bufnr].gitsigns_status = nil
|
||||
end
|
||||
|
||||
function Status:clear_diff(bufnr)
|
||||
self:update(bufnr, { added = 0, removed = 0, changed = 0 })
|
||||
self:update(bufnr, { added = 0, removed = 0, changed = 0 })
|
||||
end
|
||||
|
||||
return Status
|
||||
|
157
lua/gitsigns/subprocess.lua
generated
157
lua/gitsigns/subprocess.lua
generated
@ -1,119 +1,108 @@
|
||||
local log = require("gitsigns.debug.log")
|
||||
local guv = require("gitsigns.uv")
|
||||
local log = require('gitsigns.debug.log')
|
||||
local guv = require('gitsigns.uv')
|
||||
local uv = vim.loop
|
||||
|
||||
local M = {JobSpec = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local M = { JobSpec = {} }
|
||||
|
||||
M.job_cnt = 0
|
||||
|
||||
--- @param ... uv_pipe_t
|
||||
local function try_close(...)
|
||||
for i = 1, select('#', ...) do
|
||||
local pipe = select(i, ...)
|
||||
if pipe and not pipe:is_closing() then
|
||||
pipe:close()
|
||||
end
|
||||
end
|
||||
for i = 1, select('#', ...) do
|
||||
local pipe = select(i, ...)
|
||||
if pipe and not pipe:is_closing() then
|
||||
pipe:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @param pipe uv_pipe_t
|
||||
--- @param x string[]|string
|
||||
local function handle_writer(pipe, x)
|
||||
if type(x) == "table" then
|
||||
for i, v in ipairs(x) do
|
||||
pipe:write(v)
|
||||
if i ~= #(x) then
|
||||
pipe:write("\n")
|
||||
else
|
||||
pipe:write("\n", function()
|
||||
try_close(pipe)
|
||||
end)
|
||||
end
|
||||
if type(x) == 'table' then
|
||||
for i, v in ipairs(x) do
|
||||
pipe:write(v)
|
||||
if i ~= #x then
|
||||
pipe:write('\n')
|
||||
else
|
||||
pipe:write('\n', function()
|
||||
try_close(pipe)
|
||||
end)
|
||||
end
|
||||
elseif x then
|
||||
-- write is string
|
||||
pipe:write(x, function()
|
||||
try_close(pipe)
|
||||
end)
|
||||
end
|
||||
end
|
||||
elseif x then
|
||||
-- write is string
|
||||
pipe:write(x, function()
|
||||
try_close(pipe)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param pipe uv_pipe_t
|
||||
--- @param output string[]
|
||||
local function handle_reader(pipe, output)
|
||||
pipe:read_start(function(err, data)
|
||||
if err then
|
||||
log.eprint(err)
|
||||
end
|
||||
if data then
|
||||
output[#output + 1] = data
|
||||
else
|
||||
try_close(pipe)
|
||||
end
|
||||
end)
|
||||
pipe:read_start(function(err, data)
|
||||
if err then
|
||||
log.eprint(err)
|
||||
end
|
||||
if data then
|
||||
output[#output + 1] = data
|
||||
else
|
||||
try_close(pipe)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- @param obj table
|
||||
--- @param callback fun(_: integer, _: integer, _: string?, _: string?)
|
||||
function M.run_job(obj, callback)
|
||||
local __FUNC__ = 'run_job'
|
||||
if log.debug_mode then
|
||||
local cmd = obj.command .. ' ' .. table.concat(obj.args, ' ')
|
||||
log.dprint(cmd)
|
||||
end
|
||||
local __FUNC__ = 'run_job'
|
||||
if log.debug_mode then
|
||||
local cmd = obj.command .. ' ' .. table.concat(obj.args, ' ')
|
||||
log.dprint(cmd)
|
||||
end
|
||||
|
||||
local stdout_data = {}
|
||||
local stderr_data = {}
|
||||
local stdout_data = {}
|
||||
local stderr_data = {}
|
||||
|
||||
local stdout = guv.new_pipe(false)
|
||||
local stderr = guv.new_pipe(false)
|
||||
local stdin
|
||||
if obj.writer then
|
||||
stdin = guv.new_pipe(false)
|
||||
end
|
||||
local stdout = guv.new_pipe(false)
|
||||
local stderr = guv.new_pipe(false)
|
||||
local stdin
|
||||
if obj.writer then
|
||||
stdin = guv.new_pipe(false)
|
||||
end
|
||||
|
||||
--- @type uv_process_t?, integer|string
|
||||
local handle, _pid
|
||||
handle, _pid = vim.loop.spawn(obj.command, {
|
||||
args = obj.args,
|
||||
stdio = { stdin, stdout, stderr },
|
||||
cwd = obj.cwd,
|
||||
},
|
||||
function(code, signal)
|
||||
if handle then
|
||||
handle:close()
|
||||
end
|
||||
stdout:read_stop()
|
||||
stderr:read_stop()
|
||||
--- @type uv_process_t?, integer|string
|
||||
local handle, _pid
|
||||
handle, _pid = vim.loop.spawn(obj.command, {
|
||||
args = obj.args,
|
||||
stdio = { stdin, stdout, stderr },
|
||||
cwd = obj.cwd,
|
||||
}, function(code, signal)
|
||||
if handle then
|
||||
handle:close()
|
||||
end
|
||||
stdout:read_stop()
|
||||
stderr:read_stop()
|
||||
|
||||
try_close(stdin, stdout, stderr)
|
||||
try_close(stdin, stdout, stderr)
|
||||
|
||||
local stdout_result = #stdout_data > 0 and table.concat(stdout_data) or nil
|
||||
local stderr_result = #stderr_data > 0 and table.concat(stderr_data) or nil
|
||||
local stdout_result = #stdout_data > 0 and table.concat(stdout_data) or nil
|
||||
local stderr_result = #stderr_data > 0 and table.concat(stderr_data) or nil
|
||||
|
||||
callback(code, signal, stdout_result, stderr_result)
|
||||
end)
|
||||
callback(code, signal, stdout_result, stderr_result)
|
||||
end)
|
||||
|
||||
if not handle then
|
||||
try_close(stdin, stdout, stderr)
|
||||
error(debug.traceback('Failed to spawn process: ' .. vim.inspect(obj)))
|
||||
end
|
||||
|
||||
if not handle then
|
||||
try_close(stdin, stdout, stderr)
|
||||
error(debug.traceback("Failed to spawn process: " .. vim.inspect(obj)))
|
||||
end
|
||||
handle_reader(stdout, stdout_data)
|
||||
handle_reader(stderr, stderr_data)
|
||||
handle_writer(stdin, obj.writer)
|
||||
|
||||
handle_reader(stdout, stdout_data)
|
||||
handle_reader(stderr, stderr_data)
|
||||
handle_writer(stdin, obj.writer)
|
||||
|
||||
M.job_cnt = M.job_cnt + 1
|
||||
M.job_cnt = M.job_cnt + 1
|
||||
end
|
||||
|
||||
return M
|
||||
|
33
lua/gitsigns/test.lua
generated
33
lua/gitsigns/test.lua
generated
@ -1,35 +1,34 @@
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
local function eq(act, exp)
|
||||
assert(act == exp, string.format('%s != %s', act, exp))
|
||||
assert(act == exp, string.format('%s != %s', act, exp))
|
||||
end
|
||||
|
||||
M._tests = {}
|
||||
|
||||
M._tests.expand_format = function()
|
||||
local util = require('gitsigns.util')
|
||||
assert('hello % world % 2021' == util.expand_format('<var1> % <var2> % <var_time:%Y>', {
|
||||
var1 = 'hello', var2 = 'world', var_time = 1616838297, }))
|
||||
local util = require('gitsigns.util')
|
||||
assert('hello % world % 2021' == util.expand_format('<var1> % <var2> % <var_time:%Y>', {
|
||||
var1 = 'hello',
|
||||
var2 = 'world',
|
||||
var_time = 1616838297,
|
||||
}))
|
||||
end
|
||||
|
||||
|
||||
M._tests.test_args = function()
|
||||
local parse_args = require('gitsigns.cli.argparse').parse_args
|
||||
local parse_args = require('gitsigns.cli.argparse').parse_args
|
||||
|
||||
local pos_args, named_args = parse_args('hello there key=value, key1="a b c"')
|
||||
local pos_args, named_args = parse_args('hello there key=value, key1="a b c"')
|
||||
|
||||
eq(pos_args[1], 'hello')
|
||||
eq(pos_args[2], 'there')
|
||||
eq(named_args.key, 'value,')
|
||||
eq(named_args.key1, 'a b c')
|
||||
eq(pos_args[1], 'hello')
|
||||
eq(pos_args[2], 'there')
|
||||
eq(named_args.key, 'value,')
|
||||
eq(named_args.key1, 'a b c')
|
||||
|
||||
pos_args, named_args = parse_args('base=HEAD~1 posarg')
|
||||
pos_args, named_args = parse_args('base=HEAD~1 posarg')
|
||||
|
||||
eq(named_args.base, 'HEAD~1')
|
||||
eq(pos_args[1], 'posarg')
|
||||
eq(named_args.base, 'HEAD~1')
|
||||
eq(pos_args[1], 'posarg')
|
||||
end
|
||||
|
||||
return M
|
||||
|
252
lua/gitsigns/util.lua
generated
252
lua/gitsigns/util.lua
generated
@ -1,41 +1,37 @@
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function M.path_exists(path)
|
||||
return vim.loop.fs_stat(path) and true or false
|
||||
return vim.loop.fs_stat(path) and true or false
|
||||
end
|
||||
|
||||
local jit_os --- @type string
|
||||
|
||||
if jit then
|
||||
jit_os = jit.os:lower()
|
||||
jit_os = jit.os:lower()
|
||||
end
|
||||
|
||||
local is_unix = false
|
||||
if jit_os then
|
||||
is_unix = jit_os == 'linux' or jit_os == 'osx' or jit_os == 'bsd'
|
||||
is_unix = jit_os == 'linux' or jit_os == 'osx' or jit_os == 'bsd'
|
||||
else
|
||||
local binfmt = package.cpath:match("%p[\\|/]?%p(%a+)")
|
||||
is_unix = binfmt ~= "dll"
|
||||
local binfmt = package.cpath:match('%p[\\|/]?%p(%a+)')
|
||||
is_unix = binfmt ~= 'dll'
|
||||
end
|
||||
|
||||
--- @param file string
|
||||
--- @return string
|
||||
function M.dirname(file)
|
||||
return file:match(string.format('^(.+)%s[^%s]+', M.path_sep, M.path_sep))
|
||||
return file:match(string.format('^(.+)%s[^%s]+', M.path_sep, M.path_sep))
|
||||
end
|
||||
|
||||
--- @param file string
|
||||
--- @return string[]
|
||||
function M.file_lines(file)
|
||||
local text = {} --- @type string[]
|
||||
for line in io.lines(file) do
|
||||
text[#text + 1] = line
|
||||
end
|
||||
return text
|
||||
local text = {} --- @type string[]
|
||||
for line in io.lines(file) do
|
||||
text[#text + 1] = line
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
M.path_sep = package.config:sub(1, 1)
|
||||
@ -43,31 +39,31 @@ M.path_sep = package.config:sub(1, 1)
|
||||
--- @param bufnr integer
|
||||
--- @return string[]
|
||||
function M.buf_lines(bufnr)
|
||||
-- nvim_buf_get_lines strips carriage returns if fileformat==dos
|
||||
local buftext = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
for i = 1, #buftext do
|
||||
buftext[i] = buftext[i] .. '\r'
|
||||
end
|
||||
end
|
||||
return buftext
|
||||
-- nvim_buf_get_lines strips carriage returns if fileformat==dos
|
||||
local buftext = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
for i = 1, #buftext do
|
||||
buftext[i] = buftext[i] .. '\r'
|
||||
end
|
||||
end
|
||||
return buftext
|
||||
end
|
||||
|
||||
--- @param buf integer
|
||||
local function delete_alt(buf)
|
||||
local alt = vim.api.nvim_buf_call(buf, function()
|
||||
return vim.fn.bufnr('#')
|
||||
end)
|
||||
if alt ~= buf and alt ~= -1 then
|
||||