perf: reduce startup time
This commit is contained in:
parent
76b71f74ce
commit
f965e3bad0
|
@ -7,9 +7,6 @@ on:
|
|||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CC: clang
|
||||
|
||||
jobs:
|
||||
commit_lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -21,7 +18,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
neovim_branch: ['v0.7.2', 'v0.8.3', 'nightly']
|
||||
neovim_branch: ['v0.8.3', 'nightly']
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NEOVIM_BRANCH: ${{ matrix.neovim_branch }}
|
||||
|
@ -30,14 +27,6 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install LuaJIT
|
||||
uses: leafo/gh-actions-lua@v9
|
||||
with:
|
||||
luaVersion: "luajit-2.1.0-beta3"
|
||||
|
||||
- name: Install Luarocks
|
||||
uses: leafo/gh-actions-luarocks@v4
|
||||
|
||||
- name: Get Neovim SHA
|
||||
id: get-nvim-sha
|
||||
run: |
|
||||
|
@ -50,12 +39,6 @@ jobs:
|
|||
path: deps
|
||||
key: ${{ steps.get-nvim-sha.outputs.sha }}-${{ hashFiles('.github/workflows/ci.yml, Makefile') }}
|
||||
|
||||
- name: Install Lua Deps
|
||||
run: make lua_deps
|
||||
|
||||
- name: Check lua files are built from latest teal
|
||||
run: make tl-ensure
|
||||
|
||||
- name: Install Neovim build dependencies
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
|
@ -78,5 +61,11 @@ 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
|
||||
|
|
15
Makefile
15
Makefile
|
@ -1,9 +1,6 @@
|
|||
|
||||
export PJ_ROOT=$(PWD)
|
||||
|
||||
# Suppress built in rules. This reduces clutter when running with -d
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
FILTER ?= .*
|
||||
|
||||
LUA_VERSION := 5.1
|
||||
|
@ -13,7 +10,7 @@ NEOVIM_BRANCH ?= master
|
|||
DEPS_DIR := $(PWD)/deps/nvim-$(NEOVIM_BRANCH)
|
||||
NVIM_DIR := $(DEPS_DIR)/neovim
|
||||
|
||||
LUAROCKS := luarocks --lua-version=$(LUA_VERSION)
|
||||
LUAROCKS := $(DEPS_DIR)/luarocks/usr/bin/luarocks
|
||||
LUAROCKS_TREE := $(DEPS_DIR)/luarocks/usr
|
||||
LUAROCKS_LPATH := $(LUAROCKS_TREE)/share/lua/$(LUA_VERSION)
|
||||
LUAROCKS_INIT := eval $$($(LUAROCKS) --tree $(LUAROCKS_TREE) path) &&
|
||||
|
@ -23,25 +20,27 @@ LUAROCKS_INIT := eval $$($(LUAROCKS) --tree $(LUAROCKS_TREE) path) &&
|
|||
$(NVIM_DIR):
|
||||
@mkdir -p $(DEPS_DIR)
|
||||
git clone --depth 1 https://github.com/neovim/neovim --branch $(NEOVIM_BRANCH) $@
|
||||
@# disable LTO to reduce compile time
|
||||
make -C $@ \
|
||||
DEPS_BUILD_DIR=$(dir $(LUAROCKS_TREE)) \
|
||||
CMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
CMAKE_EXTRA_FLAGS='-DCI_BUILD=OFF -DENABLE_LTO=OFF'
|
||||
|
||||
TL := $(LUAROCKS_TREE)/bin/tl
|
||||
|
||||
$(TL):
|
||||
$(TL): $(NVIM_DIR)
|
||||
@mkdir -p $$(dirname $@)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install tl $(TL_VERSION)
|
||||
|
||||
INSPECT := $(LUAROCKS_LPATH)/inspect.lua
|
||||
|
||||
$(INSPECT):
|
||||
$(INSPECT): $(NVIM_DIR)
|
||||
@mkdir -p $$(dirname $@)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install inspect
|
||||
|
||||
LUV := $(LUAROCKS_TREE)/lib/lua/$(LUA_VERSION)/luv.so
|
||||
|
||||
$(LUV):
|
||||
$(LUV): $(NVIM_DIR)
|
||||
@mkdir -p $$(dirname $@)
|
||||
$(LUAROCKS) --tree $(LUAROCKS_TREE) install luv
|
||||
|
||||
|
|
|
@ -335,6 +335,7 @@ local function get_marker_text(marker)
|
|||
CONFIG = gen_config_doc,
|
||||
FUNCTIONS = gen_functions_doc{
|
||||
'teal/gitsigns.tl',
|
||||
'teal/gitsigns/attach.tl',
|
||||
'teal/gitsigns/actions.tl',
|
||||
},
|
||||
HIGHLIGHTS = gen_highlights_doc,
|
||||
|
|
|
@ -1,405 +1,159 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local Status = require("gitsigns.status")
|
||||
local git = require('gitsigns.git')
|
||||
local manager = require('gitsigns.manager')
|
||||
local util = require('gitsigns.util')
|
||||
local hl = require('gitsigns.highlight')
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local CacheEntry = gs_cache.CacheEntry
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local Config = gs_config.Config
|
||||
local config = gs_config.config
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprintf = gs_debug.dprintf
|
||||
local dprint = gs_debug.dprint
|
||||
|
||||
local Debounce = require("gitsigns.debounce")
|
||||
local debounce_trailing = Debounce.debounce_trailing
|
||||
local throttle_by_id = Debounce.throttle_by_id
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
local current_buf = api.nvim_get_current_buf
|
||||
local uv = require('gitsigns.uv')
|
||||
|
||||
local M = {GitContext = {}, }
|
||||
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 GitContext = M.GitContext
|
||||
|
||||
--- Detach Gitsigns from all buffers it is attached to.
|
||||
function M.detach_all()
|
||||
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
|
||||
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 current_buf()
|
||||
dprint('Detached')
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
dprint('Cache was nil')
|
||||
if #paths == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
manager.detach(bufnr, _keep_signs)
|
||||
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
|
||||
cache:destroy(bufnr)
|
||||
end
|
||||
|
||||
-- @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
|
||||
|
||||
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
|
||||
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()
|
||||
return vim.fn.expand('%:p')
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
return file
|
||||
end
|
||||
|
||||
local vimgrep_running = false
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
local function on_reload(_, bufnr)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
end
|
||||
|
||||
local function on_detach(_, bufnr)
|
||||
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
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr, file, encoding)
|
||||
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
|
||||
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'
|
||||
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)
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
dprint('Attaching')
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
|
||||
if not api.nvim_buf_is_loaded(cbuf) then
|
||||
dprint('Non-loaded buffer')
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
scheduler()
|
||||
vim.g.gitsigns_head = head
|
||||
|
||||
if not gitdir then
|
||||
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
|
||||
local towatch = gitdir .. '/HEAD'
|
||||
|
||||
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
|
||||
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
if cwd_watcher:getpath() == towatch then
|
||||
-- Already watching
|
||||
return
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
-- 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
|
||||
end
|
||||
dprint('Git cwd dir update')
|
||||
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
local new_head = git.get_repo_info(cwd).abbrev_head
|
||||
scheduler()
|
||||
end
|
||||
vim.g.gitsigns_head = new_head
|
||||
end))
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
|
||||
scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
|
||||
if vim.startswith(file, repo.gitdir .. util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
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
|
||||
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
scheduler()
|
||||
|
||||
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
|
||||
end)
|
||||
|
||||
--- Attach Gitsigns to the buffer.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {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.
|
||||
M.attach = void(function(bufnr, ctx, _trigger)
|
||||
attach_throttled(bufnr or current_buf(), ctx, _trigger)
|
||||
end)
|
||||
|
||||
local function setup_cli()
|
||||
local funcs = M
|
||||
api.nvim_create_user_command('Gitsigns', function(params)
|
||||
require('gitsigns.cli').run(funcs, params)
|
||||
require('gitsigns.cli').run(params)
|
||||
end, {
|
||||
force = true,
|
||||
nargs = '*',
|
||||
range = true,
|
||||
complete = function(arglead, line)
|
||||
return require('gitsigns.cli').complete(funcs, arglead, line)
|
||||
return require('gitsigns.cli').complete(arglead, line)
|
||||
end, })
|
||||
end
|
||||
|
||||
local function wrap_func(fn, ...)
|
||||
local args = { ... }
|
||||
local nargs = select('#', ...)
|
||||
return function()
|
||||
fn(unpack(args, 1, nargs))
|
||||
local exported = {
|
||||
'attach',
|
||||
'actions',
|
||||
}
|
||||
|
||||
local function setup_debug()
|
||||
log.debug_mode = config.debug_mode
|
||||
log.verbose = config._verbose
|
||||
|
||||
if config.debug_mode then
|
||||
exported[#exported + 1] = 'debug'
|
||||
end
|
||||
end
|
||||
|
||||
local function autocmd(event, opts)
|
||||
local opts0 = {}
|
||||
if type(opts) == "function" then
|
||||
opts0.callback = wrap_func(opts)
|
||||
else
|
||||
opts0 = opts
|
||||
local function setup_attach()
|
||||
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
|
||||
opts0.group = 'gitsigns'
|
||||
api.nvim_create_autocmd(event, opts0)
|
||||
|
||||
api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'BufWritePost' }, {
|
||||
group = 'gitsigns',
|
||||
callback = function(data)
|
||||
M.attach(nil, nil, data.event)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function on_or_after_vimenter(fn)
|
||||
if vim.v.vim_did_enter == 1 then
|
||||
fn()
|
||||
else
|
||||
api.nvim_create_autocmd('VimEnter', {
|
||||
callback = wrap_func(fn),
|
||||
once = true,
|
||||
})
|
||||
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,
|
||||
})
|
||||
end
|
||||
|
||||
--- Setup and start Gitsigns.
|
||||
|
@ -417,87 +171,36 @@ M.setup = void(function(cfg)
|
|||
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
|
||||
|
||||
gs_debug.debug_mode = config.debug_mode
|
||||
gs_debug.verbose = config._verbose
|
||||
|
||||
if config.debug_mode then
|
||||
for nm, f in pairs(gs_debug.add_debug_functions(cache)) do
|
||||
(M)[nm] = f
|
||||
end
|
||||
end
|
||||
|
||||
manager.setup()
|
||||
|
||||
Status.formatter = config.status_formatter
|
||||
|
||||
-- Make sure highlights are setup on or after VimEnter so the colorscheme is
|
||||
-- loaded. Do not set them up with vim.schedule as this removes the intro
|
||||
-- message.
|
||||
on_or_after_vimenter(hl.setup_highlights)
|
||||
|
||||
setup_debug()
|
||||
setup_cli()
|
||||
|
||||
git.enable_yadm = config.yadm.enable
|
||||
git.set_version(config._git_version)
|
||||
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
|
||||
|
||||
api.nvim_create_augroup('gitsigns', {})
|
||||
|
||||
autocmd('VimLeavePre', M.detach_all)
|
||||
autocmd('ColorScheme', hl.setup_highlights)
|
||||
autocmd('BufRead', wrap_func(M.attach, nil, nil, 'BufRead'))
|
||||
autocmd('BufNewFile', wrap_func(M.attach, nil, nil, 'BufNewFile'))
|
||||
autocmd('BufWritePost', wrap_func(M.attach, nil, nil, 'BufWritePost'))
|
||||
if config._test_mode then
|
||||
require('gitsigns.attach')._setup()
|
||||
require('gitsigns.git')._set_version(config._git_version)
|
||||
end
|
||||
|
||||
autocmd('OptionSet', {
|
||||
pattern = 'fileformat',
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end, })
|
||||
setup_attach()
|
||||
setup_cwd_head()
|
||||
|
||||
|
||||
-- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
-- waste lots of resource and even slow down vimgrep.
|
||||
autocmd('QuickFixCmdPre', {
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = true
|
||||
end,
|
||||
})
|
||||
|
||||
autocmd('QuickFixCmdPost', {
|
||||
pattern = '*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = false
|
||||
end,
|
||||
})
|
||||
|
||||
require('gitsigns.current_line_blame').setup()
|
||||
|
||||
scheduler()
|
||||
manager.update_cwd_head()
|
||||
-- Need to debounce in case some plugin changes the cwd too often
|
||||
-- (like vim-grepper)
|
||||
autocmd('DirChanged', debounce_trailing(100, manager.update_cwd_head))
|
||||
M._setup_done = true
|
||||
end)
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(_, f)
|
||||
return (require('gitsigns.actions'))[f]
|
||||
for _, mod in ipairs(exported) do
|
||||
local m = (require)('gitsigns.' .. mod)
|
||||
if m[f] then
|
||||
return m[f]
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
local async = require('gitsigns.async')
|
||||
local git = require('gitsigns.git')
|
||||
|
||||
local log = require("gitsigns.debug.log")
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
local manager = require('gitsigns.manager')
|
||||
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 gs_config = require('gitsigns.config')
|
||||
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 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
|
||||
|
||||
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
|
||||
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()
|
||||
return vim.fn.expand('%:p')
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
local function on_reload(_, bufnr)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
end
|
||||
|
||||
local function on_detach(_, bufnr)
|
||||
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
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr, file, encoding)
|
||||
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
|
||||
end
|
||||
|
||||
local done_setup = false
|
||||
|
||||
function M._setup()
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
|
||||
done_setup = true
|
||||
|
||||
manager.setup()
|
||||
|
||||
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, })
|
||||
|
||||
|
||||
-- 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,
|
||||
})
|
||||
|
||||
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'
|
||||
|
||||
M._setup()
|
||||
|
||||
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
|
||||
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
async.scheduler()
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
|
||||
async.scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
|
||||
if vim.startswith(file, repo.gitdir .. util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
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
|
||||
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
async.scheduler()
|
||||
|
||||
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
|
||||
end)
|
||||
|
||||
--- Detach Gitsigns from all buffers it is attached to.
|
||||
function M.detach_all()
|
||||
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
|
||||
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
|
||||
|
||||
manager.detach(bufnr, _keep_signs)
|
||||
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
|
||||
cache:destroy(bufnr)
|
||||
end
|
||||
|
||||
|
||||
--- Attach Gitsigns to the buffer.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {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.
|
||||
M.attach = void(function(bufnr, ctx, _trigger)
|
||||
attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger)
|
||||
end)
|
||||
|
||||
return M
|
|
@ -1,12 +1,22 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprintf = gs_debug.dprintf
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local message = require('gitsigns.message')
|
||||
|
||||
local parse_args = require('gitsigns.cli.argparse').parse_args
|
||||
|
||||
local actions = require('gitsigns.actions')
|
||||
local attach = require('gitsigns.attach')
|
||||
local gs_debug = require('gitsigns.debug')
|
||||
|
||||
local sources = {
|
||||
[actions] = true,
|
||||
[attach] = false,
|
||||
[gs_debug] = false,
|
||||
}
|
||||
|
||||
-- try to parse each argument as a lua boolean, nil or number, if fails then
|
||||
-- keep argument as a string:
|
||||
--
|
||||
|
@ -29,14 +39,13 @@ local M = {}
|
|||
|
||||
|
||||
|
||||
function M.complete(funcs, arglead, line)
|
||||
function M.complete(arglead, line)
|
||||
local words = vim.split(line, '%s+')
|
||||
local n = #words
|
||||
|
||||
local actions = require('gitsigns.actions')
|
||||
local matches = {}
|
||||
if n == 2 then
|
||||
for _, m in ipairs({ actions, funcs }) do
|
||||
for m, _ in pairs(sources) do
|
||||
for func, _ in pairs(m) do
|
||||
if not func:match('^[a-z]') then
|
||||
-- exclude
|
||||
|
@ -55,22 +64,20 @@ function M.complete(funcs, arglead, line)
|
|||
return matches
|
||||
end
|
||||
|
||||
M.run = void(function(funcs, params)
|
||||
M.run = void(function(params)
|
||||
local __FUNC__ = 'cli.run'
|
||||
local pos_args_raw, named_args_raw = parse_args(params.args)
|
||||
|
||||
local func = pos_args_raw[1]
|
||||
|
||||
if not func then
|
||||
func = async.wrap(vim.ui.select, 3)(M.complete(funcs, '', 'Gitsigns '), {})
|
||||
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 actions = require('gitsigns.actions')
|
||||
local actions0 = actions
|
||||
|
||||
dprintf("Running action '%s' with arguments %s", func, vim.inspect(args, { newline = ' ', indent = '' }))
|
||||
|
||||
local cmd_func = actions._get_cmd_func(func)
|
||||
|
@ -81,15 +88,13 @@ M.run = void(function(funcs, params)
|
|||
return
|
||||
end
|
||||
|
||||
if type(actions0[func]) == 'function' then
|
||||
actions0[func](unpack(pos_args), named_args)
|
||||
return
|
||||
end
|
||||
|
||||
if type(funcs[func]) == 'function' then
|
||||
-- Note functions here do not have named arguments
|
||||
funcs[func](unpack(pos_args))
|
||||
return
|
||||
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
|
||||
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)
|
||||
|
|
|
@ -135,6 +135,7 @@ local M = {Config = {DiffOpts = {}, SignConfig = {}, watch_gitdir = {}, current_
|
|||
|
||||
|
||||
|
||||
|
||||
M.config = {}
|
||||
|
||||
M.schema = {
|
||||
|
@ -712,6 +713,11 @@ M.schema = {
|
|||
]],
|
||||
},
|
||||
|
||||
_test_mode = {
|
||||
type = 'boolean',
|
||||
default = false,
|
||||
},
|
||||
|
||||
word_diff = {
|
||||
type = 'boolean',
|
||||
default = false,
|
||||
|
|
|
@ -1,110 +1,6 @@
|
|||
local M = {
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages = {},
|
||||
}
|
||||
local log = require('gitsigns.debug.log')
|
||||
|
||||
local function getvarvalue(name, lvl)
|
||||
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 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]
|
||||
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)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
function M.dprint(obj)
|
||||
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)
|
||||
end
|
||||
|
||||
function M.vprint(obj)
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
function M.eprint(msg)
|
||||
eprint(msg, 1)
|
||||
end
|
||||
|
||||
function M.eprintf(fmt, ...)
|
||||
eprint(fmt:format(...), 1)
|
||||
end
|
||||
local M = {}
|
||||
|
||||
local function process(raw_item, path)
|
||||
if path[#path] == vim.inspect.METATABLE then
|
||||
|
@ -123,28 +19,25 @@ local function process(raw_item, path)
|
|||
return raw_item
|
||||
end
|
||||
|
||||
function M.add_debug_functions(cache)
|
||||
local R = {}
|
||||
R.dump_cache = function()
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({ { text } }, false, {})
|
||||
return cache
|
||||
end
|
||||
function M.dump_cache()
|
||||
-- TODO(lewis6991): hack: use package.loaded to avoid circular deps
|
||||
local cache = (package.loaded['gitsigns.cache']).cache
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({ { text } }, false, {})
|
||||
return cache
|
||||
end
|
||||
|
||||
R.debug_messages = function(noecho)
|
||||
if not noecho then
|
||||
for _, m in ipairs(M.messages) do
|
||||
vim.api.nvim_echo({ { m } }, false, {})
|
||||
end
|
||||
function M.debug_messages(noecho)
|
||||
if not noecho then
|
||||
for _, m in ipairs(log.messages) do
|
||||
vim.api.nvim_echo({ { m } }, false, {})
|
||||
end
|
||||
return M.messages
|
||||
end
|
||||
return log.messages
|
||||
end
|
||||
|
||||
R.clear_debug = function()
|
||||
M.messages = {}
|
||||
end
|
||||
|
||||
return R
|
||||
function M.clear_debug()
|
||||
log.messages = {}
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
local M = {
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages = {},
|
||||
}
|
||||
|
||||
local function getvarvalue(name, lvl)
|
||||
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 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]
|
||||
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)
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
function M.dprint(obj)
|
||||
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)
|
||||
end
|
||||
|
||||
function M.vprint(obj)
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
function M.eprint(msg)
|
||||
eprint(msg, 1)
|
||||
end
|
||||
|
||||
function M.eprintf(fmt, ...)
|
||||
eprint(fmt:format(...), 1)
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,18 +1,21 @@
|
|||
local async = require('gitsigns.async')
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local gsd = require("gitsigns.debug")
|
||||
local log = require("gitsigns.debug.log")
|
||||
local util = require('gitsigns.util')
|
||||
local subprocess = require('gitsigns.subprocess')
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local config = gs_config.config
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
local Hunk = gs_hunks.Hunk
|
||||
|
||||
local uv = vim.loop
|
||||
local startswith = vim.startswith
|
||||
|
||||
local dprint = require("gitsigns.debug").dprint
|
||||
local eprint = require("gitsigns.debug").eprint
|
||||
local dprint = require('gitsigns.debug.log').dprint
|
||||
local eprint = require('gitsigns.debug.log').eprint
|
||||
local err = require('gitsigns.message').error
|
||||
|
||||
|
||||
|
@ -78,10 +81,6 @@ local M = {BlameInfo = {}, Version = {}, RepoInfo = {}, Repo = {}, FileProps = {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -167,8 +166,35 @@ local function check_version(version)
|
|||
return true
|
||||
end
|
||||
|
||||
--- @async
|
||||
function M._set_version(version)
|
||||
if version ~= 'auto' then
|
||||
M.version = parse_version(version)
|
||||
return
|
||||
end
|
||||
|
||||
local _, _, stdout, stderr = async.wait(2, subprocess.run_job, {
|
||||
command = 'git', args = { '--version' },
|
||||
})
|
||||
|
||||
local line = vim.split(stdout or '', '\n', true)[1]
|
||||
if not line then
|
||||
err("Unable to detect git version as 'git --version' failed to return anything")
|
||||
eprint(stderr)
|
||||
return
|
||||
end
|
||||
assert(type(line) == 'string', 'Unexpected output: ' .. line)
|
||||
assert(startswith(line, 'git version'), 'Unexpected output: ' .. line)
|
||||
local parts = vim.split(line, '%s+')
|
||||
M.version = parse_version(parts[3])
|
||||
end
|
||||
|
||||
|
||||
--- @async
|
||||
local git_command = async.create(function(args, spec)
|
||||
if not M.version then
|
||||
M._set_version(config._git_version)
|
||||
end
|
||||
spec = spec or {}
|
||||
spec.command = spec.command or 'git'
|
||||
spec.args = spec.command == 'git' and {
|
||||
|
@ -187,7 +213,7 @@ local git_command = async.create(function(args, spec)
|
|||
if not spec.suppress_stderr then
|
||||
if stderr then
|
||||
local cmd_str = table.concat({ spec.command, unpack(args) }, ' ')
|
||||
gsd.eprintf("Recieved stderr when running command\n'%s':\n%s", cmd_str, stderr)
|
||||
log.eprintf("Recieved stderr when running command\n'%s':\n%s", cmd_str, stderr)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -199,10 +225,10 @@ local git_command = async.create(function(args, spec)
|
|||
stdout_lines[#stdout_lines] = nil
|
||||
end
|
||||
|
||||
if gsd.verbose then
|
||||
gsd.vprintf('%d lines:', #stdout_lines)
|
||||
if log.verbose then
|
||||
log.vprintf('%d lines:', #stdout_lines)
|
||||
for i = 1, math.min(10, #stdout_lines) do
|
||||
gsd.vprintf('\t%s', stdout_lines[i])
|
||||
log.vprintf('\t%s', stdout_lines[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -235,7 +261,7 @@ local function process_abbrev_head(gitdir, head_str, path, cmd)
|
|||
suppress_stderr = true,
|
||||
cwd = path,
|
||||
})[1] or ''
|
||||
if gsd.debug_mode and short_sha ~= '' then
|
||||
if log.debug_mode and short_sha ~= '' then
|
||||
short_sha = 'HEAD'
|
||||
end
|
||||
if util.path_exists(gitdir .. '/rebase-merge') or
|
||||
|
@ -310,25 +336,6 @@ function M.get_repo_info(path, cmd, gitdir, toplevel)
|
|||
return ret
|
||||
end
|
||||
|
||||
--- @async
|
||||
function M.set_version(version)
|
||||
if version ~= 'auto' then
|
||||
M.version = parse_version(version)
|
||||
return
|
||||
end
|
||||
local results, stderr = git_command({ '--version' })
|
||||
local line = results[1]
|
||||
if not line then
|
||||
err("Unable to detect git version as 'git --version' failed to return anything")
|
||||
eprint(stderr)
|
||||
return
|
||||
end
|
||||
assert(type(line) == 'string', 'Unexpected output: ' .. line)
|
||||
assert(startswith(line, 'git version'), 'Unexpected output: ' .. line)
|
||||
local parts = vim.split(line, '%s+')
|
||||
M.version = parse_version(parts[3])
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Git repo object methods
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -444,7 +451,7 @@ function Repo.new(dir, gitdir, toplevel)
|
|||
end
|
||||
|
||||
-- Try yadm
|
||||
if M.enable_yadm and not self.gitdir then
|
||||
if config.yadm.enable and not self.gitdir then
|
||||
if vim.startswith(dir, os.getenv('HOME')) and
|
||||
#git_command({ 'ls-files', dir }, { command = 'yadm' }) ~= 0 then
|
||||
M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
|
@ -501,7 +508,7 @@ function Obj:file_info(file, silent)
|
|||
-- Suppress_stderr for the cases when we run:
|
||||
-- git ls-files --others exists/nonexist
|
||||
if not stderr:match('^warning: could not open directory .*: No such file or directory') then
|
||||
gsd.eprint(stderr)
|
||||
log.eprint(stderr)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -168,8 +168,11 @@ local function cmul(x, factor)
|
|||
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, ...)
|
||||
end
|
||||
|
||||
local function derive(hl, hldef)
|
||||
local dprintf = require("gitsigns.debug").dprintf
|
||||
for _, d in ipairs(hldef) do
|
||||
if is_hl_set(d) then
|
||||
dprintf('Deriving %s from %s', hl, d)
|
||||
|
@ -201,7 +204,6 @@ end
|
|||
-- Setup a GitSign* highlight by deriving it from other potentially present
|
||||
-- highlights.
|
||||
M.setup_highlights = function()
|
||||
local dprintf = require("gitsigns.debug").dprintf
|
||||
for _, hlg in ipairs(M.hls) do
|
||||
for hl, hldef in pairs(hlg) do
|
||||
if is_hl_set(hl) then
|
||||
|
|
|
@ -12,15 +12,14 @@ local Status = require("gitsigns.status")
|
|||
local debounce_trailing = require('gitsigns.debounce').debounce_trailing
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprint = gs_debug.dprint
|
||||
local dprintf = gs_debug.dprintf
|
||||
local eprint = gs_debug.eprint
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprint = log.dprint
|
||||
local dprintf = log.dprintf
|
||||
local eprint = log.eprint
|
||||
|
||||
local subprocess = require('gitsigns.subprocess')
|
||||
local util = require('gitsigns.util')
|
||||
local run_diff = require('gitsigns.diff')
|
||||
local git = require('gitsigns.git')
|
||||
local uv = require('gitsigns.uv')
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
|
@ -43,7 +42,6 @@ local M = {}
|
|||
|
||||
|
||||
|
||||
|
||||
local scheduler_if_buf_valid = awrap(function(buf, cb)
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
|
@ -444,67 +442,6 @@ function M.watch_gitdir(bufnr, gitdir)
|
|||
return w
|
||||
end
|
||||
|
||||
local cwd_watcher
|
||||
|
||||
M.update_cwd_head = void(function()
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head
|
||||
|
||||
-- Look in the cache first
|
||||
for _, bcache in pairs(cache) do
|
||||
local repo = bcache.git_obj.repo
|
||||
if repo.toplevel == cwd then
|
||||
head = repo.abbrev_head
|
||||
gitdir = repo.gitdir
|
||||
break
|
||||
end
|
||||
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
|
||||
|
||||
if not gitdir then
|
||||
return
|
||||
end
|
||||
|
||||
local towatch = gitdir .. '/HEAD'
|
||||
|
||||
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)
|
||||
local __FUNC__ = 'cwd_watcher_cb'
|
||||
if err then
|
||||
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)
|
||||
|
||||
function M.reset_signs()
|
||||
-- Remove all signs
|
||||
signs_normal:reset()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local config = require('gitsigns.config').config
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
|
||||
local dprint = require('gitsigns.debug').dprint
|
||||
local dprint = require('gitsigns.debug.log').dprint
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local api = vim.api
|
||||
|
||||
|
||||
local StatusObj = {}
|
||||
|
||||
|
||||
|
@ -11,7 +12,6 @@ local StatusObj = {}
|
|||
|
||||
local Status = {
|
||||
StatusObj = StatusObj,
|
||||
formatter = nil,
|
||||
}
|
||||
|
||||
function Status:update(bufnr, status)
|
||||
|
@ -24,7 +24,10 @@ function Status:update(bufnr, status)
|
|||
end
|
||||
vim.b[bufnr].gitsigns_head = status.head or ''
|
||||
vim.b[bufnr].gitsigns_status_dict = status
|
||||
vim.b[bufnr].gitsigns_status = self.formatter(status)
|
||||
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
vim.b[bufnr].gitsigns_status = config.status_formatter(status)
|
||||
end
|
||||
|
||||
function Status:clear(bufnr)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local gsd = require("gitsigns.debug")
|
||||
local log = require("gitsigns.debug.log")
|
||||
local guv = require("gitsigns.uv")
|
||||
local uv = vim.loop
|
||||
|
||||
|
@ -47,7 +47,7 @@ end
|
|||
local function handle_reader(pipe, output)
|
||||
pipe:read_start(function(err, data)
|
||||
if err then
|
||||
gsd.eprint(err)
|
||||
log.eprint(err)
|
||||
end
|
||||
if data then
|
||||
output[#output + 1] = data
|
||||
|
@ -59,9 +59,9 @@ end
|
|||
|
||||
function M.run_job(obj, callback)
|
||||
local __FUNC__ = 'run_job'
|
||||
if gsd.debug_mode then
|
||||
if log.debug_mode then
|
||||
local cmd = obj.command .. ' ' .. table.concat(obj.args, ' ')
|
||||
gsd.dprint(cmd)
|
||||
log.dprint(cmd)
|
||||
end
|
||||
|
||||
local stdout_data = {}
|
||||
|
@ -75,7 +75,7 @@ function M.run_job(obj, callback)
|
|||
end
|
||||
|
||||
local handle, pid
|
||||
handle, pid = guv.spawn(obj.command, {
|
||||
handle, pid = vim.loop.spawn(obj.command, {
|
||||
args = obj.args,
|
||||
stdio = { stdin, stdout, stderr },
|
||||
cwd = obj.cwd,
|
||||
|
|
545
teal/gitsigns.tl
545
teal/gitsigns.tl
|
@ -1,405 +1,159 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local Status = require("gitsigns.status")
|
||||
local git = require('gitsigns.git')
|
||||
local manager = require('gitsigns.manager')
|
||||
local util = require('gitsigns.util')
|
||||
local hl = require('gitsigns.highlight')
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local CacheEntry = gs_cache.CacheEntry
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local Config = gs_config.Config
|
||||
local config = gs_config.config
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprintf = gs_debug.dprintf
|
||||
local dprint = gs_debug.dprint
|
||||
|
||||
local Debounce = require("gitsigns.debounce")
|
||||
local debounce_trailing = Debounce.debounce_trailing
|
||||
local throttle_by_id = Debounce.throttle_by_id
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
local current_buf = api.nvim_get_current_buf
|
||||
local uv = require('gitsigns.uv')
|
||||
|
||||
local record M
|
||||
setup : function(cfg: Config)
|
||||
detach : function(bufnr: integer, _keep_signs: boolean)
|
||||
detach_all : function()
|
||||
attach : function(cbuf: integer, ctx: GitContext, trigger: string)
|
||||
setup: function(cfg: Config)
|
||||
|
||||
record GitContext
|
||||
toplevel: string
|
||||
gitdir: string
|
||||
file: string
|
||||
commit: string
|
||||
base: string
|
||||
end
|
||||
-- from attach.tl
|
||||
attach: function(cbuf: integer, ctx: table, trigger: string)
|
||||
|
||||
_setup_done: boolean
|
||||
end
|
||||
|
||||
local GitContext = M.GitContext
|
||||
local cwd_watcher: vim.loop.FSPollObj
|
||||
|
||||
--- Detach Gitsigns from all buffers it is attached to.
|
||||
function M.detach_all()
|
||||
for k, _ in pairs(cache as {integer:CacheEntry}) do
|
||||
M.detach(k)
|
||||
end
|
||||
end
|
||||
local update_cwd_head = void(function()
|
||||
local paths = vim.fs.find('.git', {
|
||||
limit = 1,
|
||||
upward = true,
|
||||
type = 'directory'
|
||||
})
|
||||
|
||||
--- Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not
|
||||
--- provided then the current buffer is used.
|
||||
---
|
||||
--- Parameters: ~
|
||||
--- {bufnr} (number): Buffer number
|
||||
function M.detach(bufnr: integer, _keep_signs: boolean)
|
||||
-- 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 current_buf()
|
||||
dprint('Detached')
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
dprint('Cache was nil')
|
||||
if #paths == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
manager.detach(bufnr, _keep_signs)
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head: string, string
|
||||
|
||||
cache:destroy(bufnr)
|
||||
end
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
|
||||
-- @return (string, string) Tuple of buffer name and commit
|
||||
local function parse_fugitive_uri(name: string): string, string
|
||||
if vim.fn.exists('*FugitiveReal') == 0 then
|
||||
dprint("Fugitive not installed")
|
||||
-- Look in the cache first
|
||||
for _, bcache in pairs(gs_cache.cache as {number:gs_cache.CacheEntry}) 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')
|
||||
|
||||
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
|
||||
|
||||
if not gitdir then
|
||||
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
|
||||
end
|
||||
local towatch = gitdir..'/HEAD'
|
||||
|
||||
local function parse_gitsigns_uri(name: string): string, string
|
||||
-- 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
|
||||
if cwd_watcher:getpath() == towatch then
|
||||
-- Already watching
|
||||
return
|
||||
end
|
||||
if root_path then
|
||||
name = root_path .. '/' .. rel_path
|
||||
end
|
||||
return name, commit
|
||||
end
|
||||
|
||||
local function get_buf_path(bufnr: integer): string, string
|
||||
local file =
|
||||
uv.fs_realpath(api.nvim_buf_get_name(bufnr))
|
||||
or
|
||||
api.nvim_buf_call(bufnr, function(): string
|
||||
return vim.fn.expand('%:p')
|
||||
-- Watch .git/HEAD to detect branch changes
|
||||
cwd_watcher:start(
|
||||
towatch,
|
||||
config.watch_gitdir.interval,
|
||||
void(function(err: string)
|
||||
local __FUNC__ = 'cwd_watcher_cb'
|
||||
if err then
|
||||
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)
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
return file
|
||||
end
|
||||
|
||||
local vimgrep_running = false
|
||||
|
||||
local function on_lines(_, bufnr: integer, _, first: integer, last_orig: integer, last_new: integer, byte_count: integer): boolean
|
||||
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: integer)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
end
|
||||
|
||||
local function on_detach(_, bufnr: integer)
|
||||
M.detach(bufnr, true)
|
||||
end
|
||||
|
||||
local function on_attach_pre(bufnr: integer): string, string
|
||||
local gitdir, toplevel: string, string
|
||||
if config._on_attach_pre then
|
||||
local res: any = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if res is table then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir as string
|
||||
end
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel as string
|
||||
end
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr: integer, file: string, encoding: string): git.Obj
|
||||
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
|
||||
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: integer, ctx: GitContext, aucmd: string)
|
||||
local __FUNC__ = 'attach'
|
||||
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: string
|
||||
local commit: string
|
||||
local gitdir_oap: string
|
||||
local toplevel_oap: string
|
||||
|
||||
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
|
||||
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
scheduler()
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
|
||||
scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
|
||||
if vim.startswith(file, repo.gitdir..util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
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
|
||||
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
scheduler()
|
||||
|
||||
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 as {string:any}, cbuf)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Attach Gitsigns to the buffer.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {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.
|
||||
M.attach = void(function(bufnr: integer, ctx: GitContext, _trigger: string)
|
||||
attach_throttled(bufnr or current_buf(), ctx, _trigger)
|
||||
)
|
||||
end)
|
||||
|
||||
local function setup_cli()
|
||||
local funcs = M as {string:function}
|
||||
api.nvim_create_user_command('Gitsigns', function(params: api.UserCmdParams)
|
||||
require'gitsigns.cli'.run(funcs, params)
|
||||
require'gitsigns.cli'.run(params)
|
||||
end, {
|
||||
force = true,
|
||||
nargs = '*',
|
||||
range = true,
|
||||
complete = function(arglead: string, line: string): {string}
|
||||
return require'gitsigns.cli'.complete(funcs, arglead, line)
|
||||
return require'gitsigns.cli'.complete(arglead, line)
|
||||
end})
|
||||
end
|
||||
|
||||
local function wrap_func(fn: function, ...: any): function()
|
||||
local args = {...}
|
||||
local nargs = select('#', ...)
|
||||
return function()
|
||||
fn(unpack(args, 1, nargs))
|
||||
local exported = {
|
||||
'attach',
|
||||
'actions'
|
||||
}
|
||||
|
||||
local function setup_debug()
|
||||
log.debug_mode = config.debug_mode
|
||||
log.verbose = config._verbose
|
||||
|
||||
if config.debug_mode then
|
||||
exported[#exported+1] = 'debug'
|
||||
end
|
||||
end
|
||||
|
||||
local function autocmd(event: string, opts: function|vim.api.AutoCmdOpts)
|
||||
local opts0: vim.api.AutoCmdOpts = {}
|
||||
if opts is function then
|
||||
opts0.callback = wrap_func(opts)
|
||||
else
|
||||
opts0 = opts
|
||||
local function setup_attach()
|
||||
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
|
||||
opts0.group = 'gitsigns'
|
||||
api.nvim_create_autocmd(event, opts0)
|
||||
|
||||
api.nvim_create_autocmd({'BufRead', 'BufNewFile', 'BufWritePost'}, {
|
||||
group = 'gitsigns',
|
||||
callback = function(data: vim.api.AutoCmdOpts.CallbackData)
|
||||
M.attach(nil, nil, data.event)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local function on_or_after_vimenter(fn: function)
|
||||
if vim.v.vim_did_enter == 1 then
|
||||
fn()
|
||||
else
|
||||
api.nvim_create_autocmd('VimEnter', {
|
||||
callback = wrap_func(fn),
|
||||
once = true
|
||||
})
|
||||
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
|
||||
})
|
||||
end
|
||||
|
||||
--- Setup and start Gitsigns.
|
||||
|
@ -417,87 +171,36 @@ M.setup = void(function(cfg: Config)
|
|||
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
|
||||
|
||||
gs_debug.debug_mode = config.debug_mode
|
||||
gs_debug.verbose = config._verbose
|
||||
|
||||
if config.debug_mode then
|
||||
for nm, f in pairs(gs_debug.add_debug_functions(cache)) do
|
||||
(M as {string:function})[nm] = f
|
||||
end
|
||||
end
|
||||
|
||||
manager.setup()
|
||||
|
||||
Status.formatter = config.status_formatter as function(Status.StatusObj): string
|
||||
|
||||
-- Make sure highlights are setup on or after VimEnter so the colorscheme is
|
||||
-- loaded. Do not set them up with vim.schedule as this removes the intro
|
||||
-- message.
|
||||
on_or_after_vimenter(hl.setup_highlights)
|
||||
|
||||
setup_debug()
|
||||
setup_cli()
|
||||
|
||||
git.enable_yadm = config.yadm.enable
|
||||
git.set_version(config._git_version)
|
||||
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
|
||||
|
||||
api.nvim_create_augroup('gitsigns', {})
|
||||
|
||||
autocmd('VimLeavePre' , M.detach_all)
|
||||
autocmd('ColorScheme' , hl.setup_highlights)
|
||||
autocmd('BufRead' , wrap_func(M.attach, nil, nil, 'BufRead'))
|
||||
autocmd('BufNewFile' , wrap_func(M.attach, nil, nil, 'BufNewFile'))
|
||||
autocmd('BufWritePost', wrap_func(M.attach, nil, nil, 'BufWritePost'))
|
||||
if config._test_mode then
|
||||
require'gitsigns.attach'._setup()
|
||||
require'gitsigns.git'._set_version(config._git_version)
|
||||
end
|
||||
|
||||
autocmd('OptionSet', {
|
||||
pattern = 'fileformat',
|
||||
callback = function()
|
||||
require('gitsigns.actions').refresh()
|
||||
end}
|
||||
)
|
||||
setup_attach()
|
||||
setup_cwd_head()
|
||||
|
||||
-- vimpgrep creates and deletes lots of buffers so attaching to each one will
|
||||
-- waste lots of resource and even slow down vimgrep.
|
||||
autocmd('QuickFixCmdPre', {
|
||||
pattern ='*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = true
|
||||
end
|
||||
})
|
||||
|
||||
autocmd('QuickFixCmdPost', {
|
||||
pattern ='*vimgrep*',
|
||||
callback = function()
|
||||
vimgrep_running = false
|
||||
end
|
||||
})
|
||||
|
||||
require('gitsigns.current_line_blame').setup()
|
||||
|
||||
scheduler()
|
||||
manager.update_cwd_head()
|
||||
-- Need to debounce in case some plugin changes the cwd too often
|
||||
-- (like vim-grepper)
|
||||
autocmd('DirChanged', debounce_trailing(100, manager.update_cwd_head))
|
||||
M._setup_done = true
|
||||
end)
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(_, f: string): any
|
||||
return (require('gitsigns.actions') as {string:function})[f]
|
||||
for _, mod in ipairs(exported) do
|
||||
local m = (require as function)('gitsigns.'..mod) as table
|
||||
if m[f] then
|
||||
return m[f]
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
local async = require('gitsigns.async')
|
||||
local git = require('gitsigns.git')
|
||||
|
||||
local log = require("gitsigns.debug.log")
|
||||
local dprintf = log.dprintf
|
||||
local dprint = log.dprint
|
||||
|
||||
local manager = require('gitsigns.manager')
|
||||
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 gs_config = require('gitsigns.config')
|
||||
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 api = vim.api
|
||||
local uv = vim.loop
|
||||
|
||||
local record GitContext
|
||||
toplevel: string
|
||||
gitdir: string
|
||||
file: string
|
||||
commit: string
|
||||
base: string
|
||||
end
|
||||
|
||||
local record M
|
||||
attach: function(cbuf: integer, ctx: GitContext, trigger: string)
|
||||
detach: function(bufnr: integer, _keep_signs: boolean)
|
||||
detach_all: function()
|
||||
end
|
||||
|
||||
local vimgrep_running = false
|
||||
|
||||
-- @return (string, string) Tuple of buffer name and commit
|
||||
local function parse_fugitive_uri(name: string): string, string
|
||||
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
|
||||
end
|
||||
|
||||
local function parse_gitsigns_uri(name: string): string, string
|
||||
-- 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: integer): string, string
|
||||
local file =
|
||||
uv.fs_realpath(api.nvim_buf_get_name(bufnr))
|
||||
or
|
||||
api.nvim_buf_call(bufnr, function(): string
|
||||
return vim.fn.expand('%:p')
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
return file
|
||||
end
|
||||
|
||||
local function on_lines(_, bufnr: integer, _, first: integer, last_orig: integer, last_new: integer, byte_count: integer): boolean
|
||||
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: integer)
|
||||
local __FUNC__ = 'on_reload'
|
||||
dprint('Reload')
|
||||
manager.update_debounced(bufnr)
|
||||
end
|
||||
|
||||
local function on_detach(_, bufnr: integer)
|
||||
M.detach(bufnr, true)
|
||||
end
|
||||
|
||||
local function on_attach_pre(bufnr: integer): string, string
|
||||
local gitdir, toplevel: string, string
|
||||
if config._on_attach_pre then
|
||||
local res: any = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if res is table then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir as string
|
||||
end
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel as string
|
||||
end
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr: integer, file: string, encoding: string): git.Obj
|
||||
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
|
||||
end
|
||||
|
||||
local done_setup = false
|
||||
|
||||
function M._setup()
|
||||
if done_setup then
|
||||
return
|
||||
end
|
||||
|
||||
done_setup = true
|
||||
|
||||
manager.setup()
|
||||
|
||||
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}
|
||||
)
|
||||
|
||||
-- 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
|
||||
})
|
||||
|
||||
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: integer, ctx: GitContext, aucmd: string)
|
||||
local __FUNC__ = 'attach'
|
||||
|
||||
M._setup()
|
||||
|
||||
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: string
|
||||
local commit: string
|
||||
local gitdir_oap: string
|
||||
local toplevel_oap: string
|
||||
|
||||
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
|
||||
|
||||
if vim.bo[cbuf].buftype ~= '' then
|
||||
dprint('Non-normal buffer')
|
||||
return
|
||||
end
|
||||
|
||||
file, commit = get_buf_path(cbuf)
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
if not file_dir or not util.path_exists(file_dir) then
|
||||
dprint('Not a path')
|
||||
return
|
||||
end
|
||||
|
||||
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if not git_obj and not ctx then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
async.scheduler()
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
end
|
||||
local repo = git_obj.repo
|
||||
|
||||
async.scheduler()
|
||||
Status:update(cbuf, {
|
||||
head = repo.abbrev_head,
|
||||
root = repo.toplevel,
|
||||
gitdir = repo.gitdir,
|
||||
})
|
||||
|
||||
if vim.startswith(file, repo.gitdir..util.path_sep) then
|
||||
dprint('In non-standard git dir')
|
||||
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
|
||||
|
||||
if not git_obj.relpath then
|
||||
dprint('Cannot resolve file in repo')
|
||||
return
|
||||
end
|
||||
|
||||
if not config.attach_to_untracked and git_obj.object_name == nil then
|
||||
dprint('File is untracked')
|
||||
return
|
||||
end
|
||||
|
||||
-- On windows os.tmpname() crashes in callback threads so initialise this
|
||||
-- variable on the main thread.
|
||||
async.scheduler()
|
||||
|
||||
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 as {string:any}, cbuf)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Detach Gitsigns from all buffers it is attached to.
|
||||
function M.detach_all()
|
||||
for k, _ in pairs(cache as {integer:CacheEntry}) 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
|
||||
function M.detach(bufnr: integer, _keep_signs: boolean)
|
||||
-- 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)
|
||||
|
||||
-- Clear status variables
|
||||
Status:clear(bufnr)
|
||||
|
||||
cache:destroy(bufnr)
|
||||
end
|
||||
|
||||
|
||||
--- Attach Gitsigns to the buffer.
|
||||
---
|
||||
--- Attributes: ~
|
||||
--- {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.
|
||||
M.attach = void(function(bufnr: integer, ctx: GitContext, _trigger: string)
|
||||
attach_throttled(bufnr or api.nvim_get_current_buf(), ctx, _trigger)
|
||||
end)
|
||||
|
||||
return M
|
|
@ -1,12 +1,22 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprintf = gs_debug.dprintf
|
||||
local message = require'gitsigns.message'
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprintf = log.dprintf
|
||||
local message = require'gitsigns.message'
|
||||
|
||||
local parse_args = require('gitsigns.cli.argparse').parse_args
|
||||
|
||||
local actions = require('gitsigns.actions')
|
||||
local attach = require('gitsigns.attach')
|
||||
local gs_debug = require('gitsigns.debug')
|
||||
|
||||
local sources = {
|
||||
[actions] = true,
|
||||
[attach] = false,
|
||||
[gs_debug] = false
|
||||
} as {{string:function}:boolean}
|
||||
|
||||
-- try to parse each argument as a lua boolean, nil or number, if fails then
|
||||
-- keep argument as a string:
|
||||
--
|
||||
|
@ -26,18 +36,17 @@ local function parse_to_lua(a: string): any
|
|||
end
|
||||
|
||||
local record M
|
||||
run : function(funcs: {string:function}, params: vim.api.UserCmdParams)
|
||||
run: function(params: vim.api.UserCmdParams)
|
||||
end
|
||||
|
||||
function M.complete(funcs: {string:function}, arglead: string, line: string): {string}
|
||||
function M.complete(arglead: string, line: string): {string}
|
||||
local words = vim.split(line, '%s+')
|
||||
local n: integer = #words
|
||||
|
||||
local actions = require('gitsigns.actions')
|
||||
local matches: {string} = {}
|
||||
if n == 2 then
|
||||
for _, m in ipairs{actions as {string:function}, funcs} do
|
||||
for func, _ in pairs(m) do
|
||||
for m, _ in pairs(sources) do
|
||||
for func, _ in pairs(m as {string:function}) do
|
||||
if not func:match('^[a-z]') then
|
||||
-- exclude
|
||||
elseif vim.startswith(func, arglead) then
|
||||
|
@ -55,22 +64,20 @@ function M.complete(funcs: {string:function}, arglead: string, line: string): {s
|
|||
return matches
|
||||
end
|
||||
|
||||
M.run = void(function(funcs: {string:function}, params: vim.api.UserCmdParams)
|
||||
M.run = void(function(params: vim.api.UserCmdParams)
|
||||
local __FUNC__ = 'cli.run'
|
||||
local pos_args_raw, named_args_raw = parse_args(params.args)
|
||||
|
||||
local func = pos_args_raw[1]
|
||||
|
||||
if not func then
|
||||
func = async.wrap(vim.ui.select, 3)(M.complete(funcs, '', 'Gitsigns '), {})
|
||||
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)) as {any}
|
||||
local named_args = vim.tbl_map(parse_to_lua, named_args_raw) as {string:any}
|
||||
local args = vim.tbl_extend('error', pos_args, named_args)
|
||||
|
||||
local actions = require('gitsigns.actions')
|
||||
local actions0 = actions as {string:function}
|
||||
|
||||
dprintf("Running action '%s' with arguments %s", func, vim.inspect(args, {newline=' ', indent=''}))
|
||||
|
||||
local cmd_func = actions._get_cmd_func(func)
|
||||
|
@ -81,15 +88,13 @@ M.run = void(function(funcs: {string:function}, params: vim.api.UserCmdParams)
|
|||
return
|
||||
end
|
||||
|
||||
if type(actions0[func]) == 'function' then
|
||||
actions0[func](unpack(pos_args), named_args)
|
||||
return
|
||||
end
|
||||
|
||||
if type(funcs[func]) == 'function' then
|
||||
-- Note functions here do not have named arguments
|
||||
funcs[func](unpack(pos_args))
|
||||
return
|
||||
for m, has_named in pairs(sources) do
|
||||
local f = (m as {string:any})[func]
|
||||
if f is function then
|
||||
-- Note functions here do not have named arguments
|
||||
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)
|
||||
|
|
|
@ -129,6 +129,7 @@ local record M
|
|||
|
||||
_git_version: string
|
||||
_verbose: boolean
|
||||
_test_mode: boolean
|
||||
end
|
||||
|
||||
schema: {string:SchemaElem}
|
||||
|
@ -712,6 +713,11 @@ M.schema = {
|
|||
]]
|
||||
},
|
||||
|
||||
_test_mode = {
|
||||
type = 'boolean',
|
||||
default = false,
|
||||
},
|
||||
|
||||
word_diff = {
|
||||
type = 'boolean',
|
||||
default = false,
|
||||
|
|
|
@ -1,110 +1,6 @@
|
|||
local M = {
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages: {string} = {}
|
||||
}
|
||||
local log = require'gitsigns.debug.log'
|
||||
|
||||
local function getvarvalue(name: string, lvl: integer): any
|
||||
lvl = lvl + 1
|
||||
local value: any
|
||||
local found: boolean
|
||||
|
||||
-- try local variables
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getlocal(lvl as function, i) as (string, any)
|
||||
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 as function
|
||||
i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i) as (string, any)
|
||||
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]
|
||||
end
|
||||
|
||||
local function get_context(lvl: integer): table
|
||||
lvl = lvl + 1
|
||||
local ret: table = {}
|
||||
ret.name = getvarvalue('__FUNC__', lvl) as string
|
||||
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
|
||||
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: any, lvl: integer)
|
||||
lvl = lvl + 1
|
||||
local msg = obj is string and obj or vim.inspect(obj)
|
||||
local ctx = get_context(lvl)
|
||||
local msg2: string
|
||||
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: any)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.dprintf(obj: string, ...:any)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
function M.vprint(obj: any)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.vprintf(obj: string, ...:any)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
local function eprint(msg: string, level: integer)
|
||||
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: string)
|
||||
eprint(msg, 1)
|
||||
end
|
||||
|
||||
function M.eprintf(fmt: string, ...:any)
|
||||
eprint(fmt:format(...), 1)
|
||||
end
|
||||
local M = {}
|
||||
|
||||
local function process(raw_item: any, path: {string}): any
|
||||
if path[#path] == vim.inspect.METATABLE then
|
||||
|
@ -123,28 +19,25 @@ local function process(raw_item: any, path: {string}): any
|
|||
return raw_item
|
||||
end
|
||||
|
||||
function M.add_debug_functions(cache: any): {string:function}
|
||||
local R: {string:function} = {}
|
||||
R.dump_cache = function(): any
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({{text}}, false, {})
|
||||
return cache
|
||||
end
|
||||
function M.dump_cache(): any
|
||||
-- TODO(lewis6991): hack: use package.loaded to avoid circular deps
|
||||
local cache = (package.loaded['gitsigns.cache'] as table).cache
|
||||
local text = vim.inspect(cache, { process = process })
|
||||
vim.api.nvim_echo({{text}}, false, {})
|
||||
return cache
|
||||
end
|
||||
|
||||
R.debug_messages = function(noecho: boolean): {string}
|
||||
if not noecho then
|
||||
for _, m in ipairs(M.messages) do
|
||||
vim.api.nvim_echo({{m}}, false, {})
|
||||
end
|
||||
function M.debug_messages(noecho: boolean): {string}
|
||||
if not noecho then
|
||||
for _, m in ipairs(log.messages) do
|
||||
vim.api.nvim_echo({{m}}, false, {})
|
||||
end
|
||||
return M.messages
|
||||
end
|
||||
return log.messages
|
||||
end
|
||||
|
||||
R.clear_debug = function()
|
||||
M.messages = {}
|
||||
end
|
||||
|
||||
return R
|
||||
function M.clear_debug()
|
||||
log.messages = {}
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
local M = {
|
||||
debug_mode = false,
|
||||
verbose = false,
|
||||
messages: {string} = {},
|
||||
}
|
||||
|
||||
local function getvarvalue(name: string, lvl: integer): any
|
||||
lvl = lvl + 1
|
||||
local value: any
|
||||
local found: boolean
|
||||
|
||||
-- try local variables
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getlocal(lvl as function, i) as (string, any)
|
||||
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 as function
|
||||
i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i) as (string, any)
|
||||
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]
|
||||
end
|
||||
|
||||
local function get_context(lvl: integer): table
|
||||
lvl = lvl + 1
|
||||
local ret: table = {}
|
||||
ret.name = getvarvalue('__FUNC__', lvl) as string
|
||||
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
|
||||
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: any, lvl: integer)
|
||||
lvl = lvl + 1
|
||||
local msg = obj is string and obj or vim.inspect(obj)
|
||||
local ctx = get_context(lvl)
|
||||
local msg2: string
|
||||
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: any)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.dprintf(obj: string, ...:any)
|
||||
if not M.debug_mode then return end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
function M.vprint(obj: any)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj, 2)
|
||||
end
|
||||
|
||||
function M.vprintf(obj: string, ...:any)
|
||||
if not (M.debug_mode and M.verbose) then return end
|
||||
cprint(obj:format(...), 2)
|
||||
end
|
||||
|
||||
local function eprint(msg: string, level: integer)
|
||||
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: string)
|
||||
eprint(msg, 1)
|
||||
end
|
||||
|
||||
function M.eprintf(fmt: string, ...:any)
|
||||
eprint(fmt:format(...), 1)
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,18 +1,21 @@
|
|||
local async = require('gitsigns.async')
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
local gsd = require("gitsigns.debug")
|
||||
local log = require("gitsigns.debug.log")
|
||||
local util = require('gitsigns.util')
|
||||
local subprocess = require('gitsigns.subprocess')
|
||||
|
||||
local gs_config = require('gitsigns.config')
|
||||
local config = gs_config.config
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
local Hunk = gs_hunks.Hunk
|
||||
|
||||
local uv = vim.loop
|
||||
local startswith = vim.startswith
|
||||
|
||||
local dprint = require("gitsigns.debug").dprint
|
||||
local eprint = require("gitsigns.debug").eprint
|
||||
local dprint = require('gitsigns.debug.log').dprint
|
||||
local eprint = require('gitsigns.debug.log').eprint
|
||||
local err = require('gitsigns.message').error
|
||||
|
||||
local record GJobSpec
|
||||
|
@ -56,10 +59,6 @@ local record M
|
|||
end
|
||||
version: Version
|
||||
|
||||
enable_yadm: boolean
|
||||
|
||||
set_version: function(string)
|
||||
|
||||
record RepoInfo
|
||||
gitdir: string
|
||||
toplevel: string
|
||||
|
@ -167,8 +166,35 @@ local function check_version(version: {number,number,number}): boolean
|
|||
return true
|
||||
end
|
||||
|
||||
--- @async
|
||||
function M._set_version(version: string)
|
||||
if version ~= 'auto' then
|
||||
M.version = parse_version(version)
|
||||
return
|
||||
end
|
||||
|
||||
local _, _, stdout, stderr = async.wait(2, subprocess.run_job, {
|
||||
command = 'git', args = { '--version' }
|
||||
})
|
||||
|
||||
local line = vim.split(stdout or '', '\n', true)[1]
|
||||
if not line then
|
||||
err("Unable to detect git version as 'git --version' failed to return anything")
|
||||
eprint(stderr)
|
||||
return
|
||||
end
|
||||
assert(type(line) == 'string', 'Unexpected output: '..line)
|
||||
assert(startswith(line, 'git version'), 'Unexpected output: '..line)
|
||||
local parts = vim.split(line, '%s+')
|
||||
M.version = parse_version(parts[3])
|
||||
end
|
||||
|
||||
|
||||
--- @async
|
||||
local git_command = async.create(function(args: {string}, spec: GJobSpec): {string}, string
|
||||
if not M.version then
|
||||
M._set_version(config._git_version)
|
||||
end
|
||||
spec = spec or {}
|
||||
spec.command = spec.command or 'git'
|
||||
spec.args = spec.command == 'git' and {
|
||||
|
@ -187,7 +213,7 @@ local git_command = async.create(function(args: {string}, spec: GJobSpec): {stri
|
|||
if not spec.suppress_stderr then
|
||||
if stderr then
|
||||
local cmd_str = table.concat({spec.command, unpack(args)}, ' ')
|
||||
gsd.eprintf("Recieved stderr when running command\n'%s':\n%s", cmd_str, stderr)
|
||||
log.eprintf("Recieved stderr when running command\n'%s':\n%s", cmd_str, stderr)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -199,10 +225,10 @@ local git_command = async.create(function(args: {string}, spec: GJobSpec): {stri
|
|||
stdout_lines[#stdout_lines] = nil
|
||||
end
|
||||
|
||||
if gsd.verbose then
|
||||
gsd.vprintf('%d lines:', #stdout_lines)
|
||||
if log.verbose then
|
||||
log.vprintf('%d lines:', #stdout_lines)
|
||||
for i = 1, math.min(10, #stdout_lines) do
|
||||
gsd.vprintf('\t%s', stdout_lines[i])
|
||||
log.vprintf('\t%s', stdout_lines[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -235,7 +261,7 @@ local function process_abbrev_head(gitdir: string, head_str: string, path: strin
|
|||
suppress_stderr = true,
|
||||
cwd = path,
|
||||
})[1] or ''
|
||||
if gsd.debug_mode and short_sha ~= '' then
|
||||
if log.debug_mode and short_sha ~= '' then
|
||||
short_sha = 'HEAD'
|
||||
end
|
||||
if util.path_exists(gitdir..'/rebase-merge')
|
||||
|
@ -310,25 +336,6 @@ function M.get_repo_info(path: string, cmd: string, gitdir: string, toplevel: st
|
|||
return ret
|
||||
end
|
||||
|
||||
--- @async
|
||||
function M.set_version(version: string)
|
||||
if version ~= 'auto' then
|
||||
M.version = parse_version(version)
|
||||
return
|
||||
end
|
||||
local results, stderr = git_command{'--version'}
|
||||
local line = results[1]
|
||||
if not line then
|
||||
err("Unable to detect git version as 'git --version' failed to return anything")
|
||||
eprint(stderr)
|
||||
return
|
||||
end
|
||||
assert(type(line) == 'string', 'Unexpected output: '..line)
|
||||
assert(startswith(line, 'git version'), 'Unexpected output: '..line)
|
||||
local parts = vim.split(line, '%s+')
|
||||
M.version = parse_version(parts[3])
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Git repo object methods
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -444,7 +451,7 @@ function Repo.new(dir: string, gitdir: string, toplevel: string): Repo
|
|||
end
|
||||
|
||||
-- Try yadm
|
||||
if M.enable_yadm and not self.gitdir then
|
||||
if config.yadm.enable and not self.gitdir then
|
||||
if vim.startswith(dir, os.getenv('HOME'))
|
||||
and #git_command({'ls-files', dir}, {command = 'yadm'}) ~= 0 then
|
||||
M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
|
@ -501,7 +508,7 @@ function Obj:file_info(file: string, silent: boolean): M.FileProps
|
|||
-- Suppress_stderr for the cases when we run:
|
||||
-- git ls-files --others exists/nonexist
|
||||
if not stderr:match('^warning: could not open directory .*: No such file or directory') then
|
||||
gsd.eprint(stderr)
|
||||
log.eprint(stderr)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -168,8 +168,11 @@ local function cmul(x: integer, factor: number): integer
|
|||
return math.floor(math.floor(r*factor) * 2^16 + math.floor(g*factor) * 2^8 + math.floor(b*factor))
|
||||
end
|
||||
|
||||
local function dprintf(fmt: string, ...:any)
|
||||
require('gitsigns.debug.log').dprintf(fmt, ...)
|
||||
end
|
||||
|
||||
local function derive(hl: string, hldef: Hldef)
|
||||
local dprintf = require("gitsigns.debug").dprintf
|
||||
for _, d in ipairs(hldef) do
|
||||
if is_hl_set(d) then
|
||||
dprintf('Deriving %s from %s', hl, d)
|
||||
|
@ -201,7 +204,6 @@ end
|
|||
-- Setup a GitSign* highlight by deriving it from other potentially present
|
||||
-- highlights.
|
||||
M.setup_highlights = function()
|
||||
local dprintf = require("gitsigns.debug").dprintf
|
||||
for _, hlg in ipairs(M.hls) do
|
||||
for hl, hldef in pairs(hlg) do
|
||||
if is_hl_set(hl) then
|
||||
|
|
|
@ -12,15 +12,14 @@ local Status = require("gitsigns.status")
|
|||
local debounce_trailing = require('gitsigns.debounce').debounce_trailing
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local gs_debug = require("gitsigns.debug")
|
||||
local dprint = gs_debug.dprint
|
||||
local dprintf = gs_debug.dprintf
|
||||
local eprint = gs_debug.eprint
|
||||
local log = require('gitsigns.debug.log')
|
||||
local dprint = log.dprint
|
||||
local dprintf = log.dprintf
|
||||
local eprint = log.eprint
|
||||
|
||||
local subprocess = require('gitsigns.subprocess')
|
||||
local util = require('gitsigns.util')
|
||||
local run_diff = require('gitsigns.diff')
|
||||
local git = require('gitsigns.git')
|
||||
local uv = require('gitsigns.uv')
|
||||
|
||||
local gs_hunks = require("gitsigns.hunks")
|
||||
|
@ -38,7 +37,6 @@ local record M
|
|||
update_debounced : function(bufnr: integer, CacheEntry)
|
||||
on_lines : function(buf: integer, first: integer, last_orig: integer, last_new: integer): boolean
|
||||
watch_gitdir : function(bufnr: integer, gitdir: string): vim.loop.FSPollObj
|
||||
update_cwd_head : function()
|
||||
detach : function(bufnr: integer, keep_signs: boolean)
|
||||
reset_signs : function()
|
||||
setup : function()
|
||||
|
@ -444,67 +442,6 @@ function M.watch_gitdir(bufnr: integer, gitdir: string): vim.loop.FSPollObj
|
|||
return w
|
||||
end
|
||||
|
||||
local cwd_watcher: vim.loop.FSPollObj
|
||||
|
||||
M.update_cwd_head = void(function()
|
||||
if cwd_watcher then
|
||||
cwd_watcher:stop()
|
||||
else
|
||||
cwd_watcher = uv.new_fs_poll(true)
|
||||
end
|
||||
|
||||
local cwd = vim.loop.cwd()
|
||||
local gitdir, head: string, string
|
||||
|
||||
-- Look in the cache first
|
||||
for _, bcache in pairs(cache as {number:CacheEntry}) do
|
||||
local repo = bcache.git_obj.repo
|
||||
if repo.toplevel == cwd then
|
||||
head = repo.abbrev_head
|
||||
gitdir = repo.gitdir
|
||||
break
|
||||
end
|
||||
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
|
||||
|
||||
if not gitdir then
|
||||
return
|
||||
end
|
||||
|
||||
local towatch = gitdir..'/HEAD'
|
||||
|
||||
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: string)
|
||||
local __FUNC__ = 'cwd_watcher_cb'
|
||||
if err then
|
||||
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)
|
||||
|
||||
function M.reset_signs()
|
||||
-- Remove all signs
|
||||
signs_normal:reset()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local config = require('gitsigns.config').config
|
||||
local SignsConfig = require('gitsigns.config').Config.SignsConfig
|
||||
|
||||
local dprint = require('gitsigns.debug').dprint
|
||||
local dprint = require('gitsigns.debug.log').dprint
|
||||
|
||||
local B = require('gitsigns.signs.base')
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local api = vim.api
|
||||
|
||||
|
||||
local record StatusObj
|
||||
added : integer
|
||||
removed : integer
|
||||
|
@ -11,7 +12,6 @@ end
|
|||
|
||||
local Status = {
|
||||
StatusObj = StatusObj,
|
||||
formatter: function(StatusObj): string = nil
|
||||
}
|
||||
|
||||
function Status:update(bufnr: integer, status: StatusObj)
|
||||
|
@ -24,7 +24,10 @@ function Status:update(bufnr: integer, status: StatusObj)
|
|||
end
|
||||
vim.b[bufnr].gitsigns_head = status.head or ''
|
||||
vim.b[bufnr].gitsigns_status_dict = status
|
||||
vim.b[bufnr].gitsigns_status = self.formatter(status)
|
||||
|
||||
local config = require('gitsigns.config').config
|
||||
|
||||
vim.b[bufnr].gitsigns_status = config.status_formatter(status)
|
||||
end
|
||||
|
||||
function Status:clear(bufnr: integer)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local gsd = require("gitsigns.debug")
|
||||
local log = require("gitsigns.debug.log")
|
||||
local guv = require("gitsigns.uv")
|
||||
local uv = vim.loop
|
||||
|
||||
|
@ -47,7 +47,7 @@ end
|
|||
local function handle_reader(pipe: uv.Pipe, output: {string})
|
||||
pipe:read_start(function(err: string, data: string)
|
||||
if err then
|
||||
gsd.eprint(err)
|
||||
log.eprint(err)
|
||||
end
|
||||
if data then
|
||||
output[#output+1] = data
|
||||
|
@ -59,9 +59,9 @@ end
|
|||
|
||||
function M.run_job(obj: M.JobSpec, callback: function(integer, integer, string, string))
|
||||
local __FUNC__ = 'run_job'
|
||||
if gsd.debug_mode then
|
||||
if log.debug_mode then
|
||||
local cmd: string = obj.command..' '..table.concat(obj.args, ' ')
|
||||
gsd.dprint(cmd)
|
||||
log.dprint(cmd)
|
||||
end
|
||||
|
||||
local stdout_data: {string} = {}
|
||||
|
@ -75,7 +75,7 @@ function M.run_job(obj: M.JobSpec, callback: function(integer, integer, string,
|
|||
end
|
||||
|
||||
local handle, pid: uv.Process, integer
|
||||
handle, pid = guv.spawn(obj.command, {
|
||||
handle, pid = vim.loop.spawn(obj.command, {
|
||||
args = obj.args,
|
||||
stdio = { stdin, stdout, stderr },
|
||||
cwd = obj.cwd
|
||||
|
|
|
@ -41,30 +41,26 @@ describe('gitdir_watcher', function()
|
|||
end)
|
||||
|
||||
it('can follow moved files', function()
|
||||
local screen = Screen.new(20, 17)
|
||||
screen:attach({ext_messages=false})
|
||||
setup_test_repo()
|
||||
setup_gitsigns(test_config)
|
||||
command('Gitsigns clear_debug')
|
||||
edit(test_file)
|
||||
|
||||
local test_file2 = test_file..'2'
|
||||
local test_file3 = test_file..'3'
|
||||
|
||||
match_debug_messages {
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
p"run_job: git .* config user.name",
|
||||
p"run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD",
|
||||
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
|
||||
'watch_gitdir(1): Watching git dir',
|
||||
p'run_job: git .* show :0:dummy.txt',
|
||||
'update(1): updates: 1, jobs: 6',
|
||||
'update(1): updates: 1, jobs: 5',
|
||||
}
|
||||
|
||||
eq({[1] = test_file}, get_bufs())
|
||||
|
||||
command('Gitsigns clear_debug')
|
||||
|
||||
local test_file2 = test_file..'2'
|
||||
git{'mv', test_file, test_file2}
|
||||
|
||||
match_debug_messages {
|
||||
|
@ -76,13 +72,14 @@ describe('gitdir_watcher', function()
|
|||
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file2)),
|
||||
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt to .*/dummy.txt2',
|
||||
p'run_job: git .* show :0:dummy.txt2',
|
||||
'update(1): updates: 2, jobs: 11'
|
||||
'update(1): updates: 2, jobs: 10'
|
||||
}
|
||||
|
||||
eq({[1] = test_file2}, get_bufs())
|
||||
|
||||
command('Gitsigns clear_debug')
|
||||
|
||||
local test_file3 = test_file..'3'
|
||||
git{'mv', test_file2, test_file3}
|
||||
|
||||
match_debug_messages {
|
||||
|
@ -94,7 +91,7 @@ describe('gitdir_watcher', function()
|
|||
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file3)),
|
||||
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt2 to .*/dummy.txt3',
|
||||
p'run_job: git .* show :0:dummy.txt3',
|
||||
'update(1): updates: 3, jobs: 16'
|
||||
'update(1): updates: 3, jobs: 15'
|
||||
}
|
||||
|
||||
eq({[1] = test_file3}, get_bufs())
|
||||
|
@ -113,7 +110,7 @@ describe('gitdir_watcher', function()
|
|||
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
|
||||
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt3 to .*/dummy.txt',
|
||||
p'run_job: git .* show :0:dummy.txt',
|
||||
'update(1): updates: 4, jobs: 22'
|
||||
'update(1): updates: 4, jobs: 21'
|
||||
}
|
||||
|
||||
eq({[1] = test_file}, get_bufs())
|
||||
|
|
|
@ -83,18 +83,18 @@ describe('gitsigns', function()
|
|||
-- Don't set this too low, or else the test will lock up
|
||||
config.watch_gitdir = {interval = 100}
|
||||
setup_gitsigns(config)
|
||||
command('Gitsigns clear_debug')
|
||||
edit(test_file)
|
||||
|
||||
expectf(function()
|
||||
match_dag(debug_messages(), {
|
||||
p'run_job: git .* %-%-version',
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
p'run_job: git .* config user.name',
|
||||
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
|
||||
p('run_job: git .* ls%-files %-%-stage %-%-others %-%-exclude%-standard %-%-eol '..helpers.pesc(test_file)),
|
||||
'watch_gitdir(1): Watching git dir',
|
||||
p'run_job: git .* show :0:dummy.txt',
|
||||
'update(1): updates: 1, jobs: 7'
|
||||
'update(1): updates: 1, jobs: 6'
|
||||
})
|
||||
end)
|
||||
|
||||
|
@ -118,7 +118,7 @@ describe('gitsigns', function()
|
|||
edit(tmpfile)
|
||||
|
||||
match_debug_messages {
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
p'run_job: git .* config user.name',
|
||||
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
|
||||
'new: Not in git repo',
|
||||
|
@ -167,7 +167,7 @@ describe('gitsigns', function()
|
|||
edit(scratch..'/.git/index')
|
||||
|
||||
match_debug_messages {
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
'new: In git dir',
|
||||
'attach(1): Empty git obj'
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ describe('gitsigns', function()
|
|||
edit(ignored_file)
|
||||
|
||||
match_debug_messages {
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
p'run_job: git .* config user.name',
|
||||
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
|
||||
p'run_job: git .* ls%-files .*/dummy_ignored.txt',
|
||||
|
@ -225,7 +225,7 @@ describe('gitsigns', function()
|
|||
command("Gitsigns clear_debug")
|
||||
command("copen")
|
||||
match_debug_messages {
|
||||
'attach(2): Attaching (trigger=BufRead)',
|
||||
'attach(2): Attaching (trigger=BufReadPost)',
|
||||
'attach(2): Non-normal buffer',
|
||||
}
|
||||
end)
|
||||
|
@ -349,7 +349,7 @@ describe('gitsigns', function()
|
|||
|
||||
edit(test_file)
|
||||
match_debug_messages {
|
||||
'attach(1): Attaching (trigger=BufRead)',
|
||||
'attach(1): Attaching (trigger=BufReadPost)',
|
||||
p'run_job: git .* config user.name',
|
||||
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
|
||||
p'run_job: git .* rev%-parse %-%-short HEAD',
|
||||
|
@ -484,7 +484,7 @@ describe('gitsigns', function()
|
|||
table.insert(messages, p'run_job: git .* diff .* /tmp/lua_.* /tmp/lua_.*')
|
||||
end
|
||||
|
||||
local jobs = internal_diff and 9 or 10
|
||||
local jobs = internal_diff and 8 or 9
|
||||
table.insert(messages, "update(1): updates: 1, jobs: "..jobs)
|
||||
|
||||
match_debug_messages(messages)
|
||||
|
|
|
@ -21,6 +21,7 @@ M.newfile = M.scratch.."/newfile.txt"
|
|||
|
||||
M.test_config = {
|
||||
debug_mode = true,
|
||||
_test_mode = true,
|
||||
signs = {
|
||||
add = {hl = 'DiffAdd' , text = '+'},
|
||||
delete = {hl = 'DiffDelete', text = '_'},
|
||||
|
@ -47,15 +48,6 @@ local test_file_text = {
|
|||
'content', 'doesn\'t', 'matter,', 'it', 'just', 'needs', 'to', 'be', 'static.'
|
||||
}
|
||||
|
||||
--- Escapes magic chars in |lua-patterns|.
|
||||
---
|
||||
---@see https://github.com/rxi/lume
|
||||
---@param s string String to escape
|
||||
---@return string %-escaped pattern string
|
||||
function M.pesc(s)
|
||||
return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
|
||||
end
|
||||
|
||||
function M.git(args)
|
||||
system{"git", "-C", M.scratch, unpack(args)}
|
||||
end
|
||||
|
@ -64,7 +56,6 @@ function M.cleanup()
|
|||
system{"rm", "-rf", M.scratch}
|
||||
end
|
||||
|
||||
|
||||
function M.setup_git()
|
||||
M.git{"init", '-b', 'master'}
|
||||
|
||||
|
@ -105,7 +96,8 @@ function M.expectf(cond, interval)
|
|||
local duration = 0
|
||||
interval = interval or 1
|
||||
while duration < timeout do
|
||||
if pcall(cond) then
|
||||
local ok, ret = pcall(cond)
|
||||
if ok and (ret == nil or ret == true) then
|
||||
return
|
||||
end
|
||||
duration = duration + interval
|
||||
|
@ -124,7 +116,7 @@ function M.edit(path)
|
|||
end
|
||||
|
||||
function M.write_to_file(path, text)
|
||||
local f = io.open(path, 'wb')
|
||||
local f = assert(io.open(path, 'wb'))
|
||||
for _, l in ipairs(text) do
|
||||
f:write(l)
|
||||
f:write('\n')
|
||||
|
@ -132,6 +124,13 @@ function M.write_to_file(path, text)
|
|||
f:close()
|
||||
end
|
||||
|
||||
local function spec_text(s)
|
||||
if type(s) == 'table' then
|
||||
return s.text
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
function M.match_lines(lines, spec)
|
||||
local i = 1
|
||||
for lid, line in ipairs(lines) do
|
||||
|
@ -153,12 +152,13 @@ function M.match_lines(lines, spec)
|
|||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if i < #spec + 1 then
|
||||
local msg = {'lines:'}
|
||||
for _, l in ipairs(lines) do
|
||||
msg[#msg+1] = string.format( '"%s"', l)
|
||||
msg[#msg+1] = string.format(' - "%s"', l)
|
||||
end
|
||||
error(('Did not match pattern \'%s\' with %s'):format(spec[i], table.concat(msg, '\n')))
|
||||
error(('Did not match pattern \'%s\' with %s'):format(spec_text(spec[i]), table.concat(msg, '\n')))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -209,7 +209,7 @@ function M.n(str)
|
|||
end
|
||||
|
||||
function M.debug_messages()
|
||||
return exec_lua("return require'gitsigns'.debug_messages(true)")
|
||||
return exec_lua("return require'gitsigns.debug.log'.messages")
|
||||
end
|
||||
|
||||
function M.match_dag(lines, spec)
|
||||
|
@ -224,6 +224,8 @@ function M.match_debug_messages(spec)
|
|||
end)
|
||||
end
|
||||
|
||||
local git_version
|
||||
|
||||
function M.setup_gitsigns(config, extra)
|
||||
extra = extra or ''
|
||||
exec_lua([[
|
||||
|
@ -232,7 +234,7 @@ function M.setup_gitsigns(config, extra)
|
|||
require('gitsigns').setup(...)
|
||||
]], config)
|
||||
M.expectf(function()
|
||||
exec_capture('au gitsigns')
|
||||
return exec_lua[[return require'gitsigns'._setup_done == true]]
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ describe('highlights', function()
|
|||
config.signs.topdelete.hl = nil
|
||||
config.numhl = true
|
||||
config.linehl = true
|
||||
config._test_mode = true
|
||||
|
||||
exec_lua('gs.setup(...)', config)
|
||||
|
||||
|
|
|
@ -226,6 +226,10 @@ local record M
|
|||
end
|
||||
|
||||
is_thread: function(): boolean
|
||||
|
||||
record fs
|
||||
find: function(string|{string}, table): {string}
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -92,7 +92,16 @@ local record M
|
|||
nvim_create_augroup: function(string, AugroupOpts): integer
|
||||
|
||||
record AutoCmdOpts
|
||||
callback: function()
|
||||
record CallbackData
|
||||
id: integer
|
||||
group: integer
|
||||
event: string
|
||||
match: string
|
||||
buf: integer
|
||||
file: string
|
||||
data: any
|
||||
end
|
||||
callback: function(CallbackData)
|
||||
command: string
|
||||
group: integer|string
|
||||
pattern: string|{string}
|
||||
|
|
Loading…
Reference in New Issue