mirror of
https://github.com/zoriya/telescope.nvim.git
synced 2026-06-05 03:49:36 +00:00
0dfe9e1edf
Original issue: When selecting files in telescope iti is impossible to enter paths from compiler that includes cursor locations, you need to clear it from the path. This commit fixes the problem by stripping out the location from path (important: only for file pickes) and using this location to set the cursor when openning the file and highlight the line or poisition in the previewer. This feature requires a new option for a picker, whichi for now is basically enabling location stripping but it is helpful for any file picker builtin or external one. By default equals `false` becuase most of pickers like live grep, current buffer fuzy are messed up with locations stripping. It is only useful for file searches. Added test suite that covers alghoritm of stripping the location to the `utils_spec.lua`
503 lines
15 KiB
Lua
503 lines
15 KiB
Lua
local actions = require "telescope.actions"
|
|
local action_state = require "telescope.actions.state"
|
|
local finders = require "telescope.finders"
|
|
local make_entry = require "telescope.make_entry"
|
|
local operators = require "telescope.operators"
|
|
local pickers = require "telescope.pickers"
|
|
local previewers = require "telescope.previewers"
|
|
local utils = require "telescope.utils"
|
|
local entry_display = require "telescope.pickers.entry_display"
|
|
local strings = require "plenary.strings"
|
|
local Path = require "plenary.path"
|
|
|
|
local conf = require("telescope.config").values
|
|
local git_command = utils.__git_command
|
|
|
|
local git = {}
|
|
|
|
local get_git_command_output = function(args, opts)
|
|
return utils.get_os_command_output(git_command(args, opts), opts.cwd)
|
|
end
|
|
|
|
git.files = function(opts)
|
|
if opts.is_bare then
|
|
utils.notify("builtin.git_files", {
|
|
msg = "This operation must be run in a work tree",
|
|
level = "ERROR",
|
|
})
|
|
return
|
|
end
|
|
|
|
local show_untracked = vim.F.if_nil(opts.show_untracked, false)
|
|
local recurse_submodules = vim.F.if_nil(opts.recurse_submodules, false)
|
|
if show_untracked and recurse_submodules then
|
|
utils.notify("builtin.git_files", {
|
|
msg = "Git does not support both --others and --recurse-submodules",
|
|
level = "ERROR",
|
|
})
|
|
return
|
|
end
|
|
|
|
-- By creating the entry maker after the cwd options,
|
|
-- we ensure the maker uses the cwd options when being created.
|
|
opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_file(opts))
|
|
opts.git_command = vim.F.if_nil(opts.git_command, git_command({ "ls-files", "--exclude-standard", "--cached" }, opts))
|
|
|
|
pickers
|
|
.new(opts, {
|
|
prompt_title = "Git Files",
|
|
files_picker = true,
|
|
finder = finders.new_oneshot_job(
|
|
vim.tbl_flatten {
|
|
opts.git_command,
|
|
show_untracked and "--others" or nil,
|
|
recurse_submodules and "--recurse-submodules" or nil,
|
|
},
|
|
opts
|
|
),
|
|
previewer = conf.file_previewer(opts),
|
|
sorter = conf.file_sorter(opts),
|
|
})
|
|
:find()
|
|
end
|
|
|
|
git.commits = function(opts)
|
|
opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts))
|
|
opts.git_command =
|
|
vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--", "." }, opts))
|
|
|
|
pickers
|
|
.new(opts, {
|
|
prompt_title = "Git Commits",
|
|
finder = finders.new_oneshot_job(opts.git_command, opts),
|
|
previewer = {
|
|
previewers.git_commit_diff_to_parent.new(opts),
|
|
previewers.git_commit_diff_to_head.new(opts),
|
|
previewers.git_commit_diff_as_was.new(opts),
|
|
previewers.git_commit_message.new(opts),
|
|
},
|
|
sorter = conf.file_sorter(opts),
|
|
attach_mappings = function(_, map)
|
|
actions.select_default:replace(actions.git_checkout)
|
|
map({ "i", "n" }, "<c-r>m", actions.git_reset_mixed)
|
|
map({ "i", "n" }, "<c-r>s", actions.git_reset_soft)
|
|
map({ "i", "n" }, "<c-r>h", actions.git_reset_hard)
|
|
return true
|
|
end,
|
|
})
|
|
:find()
|
|
end
|
|
|
|
git.stash = function(opts)
|
|
opts.show_branch = vim.F.if_nil(opts.show_branch, true)
|
|
opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_stash(opts))
|
|
opts.git_command = vim.F.if_nil(opts.git_command, git_command({ "--no-pager", "stash", "list" }, opts))
|
|
|
|
pickers
|
|
.new(opts, {
|
|
prompt_title = "Git Stash",
|
|
finder = finders.new_oneshot_job(opts.git_command, opts),
|
|
previewer = previewers.git_stash_diff.new(opts),
|
|
sorter = conf.file_sorter(opts),
|
|
attach_mappings = function()
|
|
actions.select_default:replace(actions.git_apply_stash)
|
|
return true
|
|
end,
|
|
})
|
|
:find()
|
|
end
|
|
|
|
local get_current_buf_line = function(winnr)
|
|
local lnum = vim.api.nvim_win_get_cursor(winnr)[1]
|
|
return vim.trim(vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(winnr), lnum - 1, lnum, false)[1])
|
|
end
|
|
|
|
local bcommits_picker = function(opts, title, finder)
|
|
return pickers.new(opts, {
|
|
prompt_title = title,
|
|
finder = finder,
|
|
previewer = {
|
|
previewers.git_commit_diff_to_parent.new(opts),
|
|
previewers.git_commit_diff_to_head.new(opts),
|
|
previewers.git_commit_diff_as_was.new(opts),
|
|
previewers.git_commit_message.new(opts),
|
|
},
|
|
sorter = conf.file_sorter(opts),
|
|
attach_mappings = function()
|
|
actions.select_default:replace(actions.git_checkout_current_buffer)
|
|
local transfrom_file = function()
|
|
return opts.current_file and Path:new(opts.current_file):make_relative(opts.cwd) or ""
|
|
end
|
|
|
|
local get_buffer_of_orig = function(selection)
|
|
local value = selection.value .. ":" .. transfrom_file()
|
|
local content = utils.get_os_command_output({ "git", "--no-pager", "show", value }, opts.cwd)
|
|
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, content)
|
|
vim.api.nvim_buf_set_name(bufnr, "Original")
|
|
return bufnr
|
|
end
|
|
|
|
local vimdiff = function(selection, command)
|
|
local ft = vim.bo.filetype
|
|
vim.cmd "diffthis"
|
|
|
|
local bufnr = get_buffer_of_orig(selection)
|
|
vim.cmd(string.format("%s %s", command, bufnr))
|
|
vim.bo.filetype = ft
|
|
vim.cmd "diffthis"
|
|
|
|
vim.api.nvim_create_autocmd("WinClosed", {
|
|
buffer = bufnr,
|
|
nested = true,
|
|
once = true,
|
|
callback = function()
|
|
vim.api.nvim_buf_delete(bufnr, { force = true })
|
|
end,
|
|
})
|
|
end
|
|
|
|
actions.select_vertical:replace(function(prompt_bufnr)
|
|
actions.close(prompt_bufnr)
|
|
local selection = action_state.get_selected_entry()
|
|
vimdiff(selection, "leftabove vert sbuffer")
|
|
end)
|
|
|
|
actions.select_horizontal:replace(function(prompt_bufnr)
|
|
actions.close(prompt_bufnr)
|
|
local selection = action_state.get_selected_entry()
|
|
vimdiff(selection, "belowright sbuffer")
|
|
end)
|
|
|
|
actions.select_tab:replace(function(prompt_bufnr)
|
|
actions.close(prompt_bufnr)
|
|
local selection = action_state.get_selected_entry()
|
|
vim.cmd("tabedit " .. transfrom_file())
|
|
vimdiff(selection, "leftabove vert sbuffer")
|
|
end)
|
|
return true
|
|
end,
|
|
})
|
|
end
|
|
|
|
git.bcommits = function(opts)
|
|
opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil
|
|
opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr))
|
|
opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts))
|
|
opts.git_command =
|
|
vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--follow" }, opts))
|
|
|
|
local title = "Git BCommits"
|
|
local finder = finders.new_oneshot_job(
|
|
vim.tbl_flatten {
|
|
opts.git_command,
|
|
opts.current_file,
|
|
},
|
|
opts
|
|
)
|
|
bcommits_picker(opts, title, finder):find()
|
|
end
|
|
|
|
git.bcommits_range = function(opts)
|
|
opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil
|
|
opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr))
|
|
opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts))
|
|
opts.git_command = vim.F.if_nil(
|
|
opts.git_command,
|
|
git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--no-patch", "-L" }, opts)
|
|
)
|
|
local visual = string.find(vim.fn.mode(), "[vV]") ~= nil
|
|
|
|
local line_number_first = opts.from
|
|
local line_number_last = vim.F.if_nil(opts.to, line_number_first)
|
|
if visual then
|
|
line_number_first = vim.F.if_nil(line_number_first, vim.fn.line "v")
|
|
line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".")
|
|
elseif opts.operator then
|
|
opts.operator = false
|
|
opts.operator_callback = true
|
|
operators.run_operator(git.bcommits_range, opts)
|
|
return
|
|
elseif opts.operator_callback then
|
|
line_number_first = vim.fn.line "'["
|
|
line_number_last = vim.fn.line "']"
|
|
elseif line_number_first == nil then
|
|
line_number_first = vim.F.if_nil(line_number_first, vim.fn.line ".")
|
|
line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".")
|
|
end
|
|
local line_range =
|
|
string.format("%d,%d:%s", line_number_first, line_number_last, Path:new(opts.current_file):make_relative(opts.cwd))
|
|
|
|
local title = "Git BCommits in range"
|
|
local finder = finders.new_oneshot_job(
|
|
vim.tbl_flatten {
|
|
opts.git_command,
|
|
line_range,
|
|
},
|
|
opts
|
|
)
|
|
bcommits_picker(opts, title, finder):find()
|
|
end
|
|
|
|
git.branches = function(opts)
|
|
local format = "%(HEAD)"
|
|
.. "%(refname)"
|
|
.. "%(authorname)"
|
|
.. "%(upstream:lstrip=2)"
|
|
.. "%(committerdate:format-local:%Y/%m/%d %H:%M:%S)"
|
|
|
|
local output = get_git_command_output(
|
|
{ "for-each-ref", "--perl", "--format", format, "--sort", "-authordate", opts.pattern },
|
|
opts
|
|
)
|
|
|
|
local show_remote_tracking_branches = vim.F.if_nil(opts.show_remote_tracking_branches, true)
|
|
|
|
local results = {}
|
|
local widths = {
|
|
name = 0,
|
|
authorname = 0,
|
|
upstream = 0,
|
|
committerdate = 0,
|
|
}
|
|
local unescape_single_quote = function(v)
|
|
return string.gsub(v, "\\([\\'])", "%1")
|
|
end
|
|
local parse_line = function(line)
|
|
local fields = vim.split(string.sub(line, 2, -2), "''")
|
|
local entry = {
|
|
head = fields[1],
|
|
refname = unescape_single_quote(fields[2]),
|
|
authorname = unescape_single_quote(fields[3]),
|
|
upstream = unescape_single_quote(fields[4]),
|
|
committerdate = fields[5],
|
|
}
|
|
local prefix
|
|
if vim.startswith(entry.refname, "refs/remotes/") then
|
|
if show_remote_tracking_branches then
|
|
prefix = "refs/remotes/"
|
|
else
|
|
return
|
|
end
|
|
elseif vim.startswith(entry.refname, "refs/heads/") then
|
|
prefix = "refs/heads/"
|
|
else
|
|
return
|
|
end
|
|
local index = 1
|
|
if entry.head ~= "*" then
|
|
index = #results + 1
|
|
end
|
|
|
|
entry.name = string.sub(entry.refname, string.len(prefix) + 1)
|
|
for key, value in pairs(widths) do
|
|
widths[key] = math.max(value, strings.strdisplaywidth(entry[key] or ""))
|
|
end
|
|
if string.len(entry.upstream) > 0 then
|
|
widths.upstream_indicator = 2
|
|
end
|
|
table.insert(results, index, entry)
|
|
end
|
|
for _, line in ipairs(output) do
|
|
parse_line(line)
|
|
end
|
|
if #results == 0 then
|
|
return
|
|
end
|
|
|
|
local displayer = entry_display.create {
|
|
separator = " ",
|
|
items = {
|
|
{ width = 1 },
|
|
{ width = widths.name },
|
|
{ width = widths.authorname },
|
|
{ width = widths.upstream_indicator },
|
|
{ width = widths.upstream },
|
|
{ width = widths.committerdate },
|
|
},
|
|
}
|
|
|
|
local make_display = function(entry)
|
|
return displayer {
|
|
{ entry.head },
|
|
{ entry.name, "TelescopeResultsIdentifier" },
|
|
{ entry.authorname },
|
|
{ string.len(entry.upstream) > 0 and "=>" or "" },
|
|
{ entry.upstream, "TelescopeResultsIdentifier" },
|
|
{ entry.committerdate },
|
|
}
|
|
end
|
|
|
|
pickers
|
|
.new(opts, {
|
|
prompt_title = "Git Branches",
|
|
finder = finders.new_table {
|
|
results = results,
|
|
entry_maker = function(entry)
|
|
entry.value = entry.name
|
|
entry.ordinal = entry.name
|
|
entry.display = make_display
|
|
return make_entry.set_default_entry_mt(entry, opts)
|
|
end,
|
|
},
|
|
previewer = previewers.git_branch_log.new(opts),
|
|
sorter = conf.file_sorter(opts),
|
|
attach_mappings = function(_, map)
|
|
actions.select_default:replace(actions.git_checkout)
|
|
map({ "i", "n" }, "<c-t>", actions.git_track_branch)
|
|
map({ "i", "n" }, "<c-r>", actions.git_rebase_branch)
|
|
map({ "i", "n" }, "<c-a>", actions.git_create_branch)
|
|
map({ "i", "n" }, "<c-s>", actions.git_switch_branch)
|
|
map({ "i", "n" }, "<c-d>", actions.git_delete_branch)
|
|
map({ "i", "n" }, "<c-y>", actions.git_merge_branch)
|
|
return true
|
|
end,
|
|
})
|
|
:find()
|
|
end
|
|
|
|
git.status = function(opts)
|
|
if opts.is_bare then
|
|
utils.notify("builtin.git_status", {
|
|
msg = "This operation must be run in a work tree",
|
|
level = "ERROR",
|
|
})
|
|
return
|
|
end
|
|
|
|
local gen_new_finder = function()
|
|
local expand_dir = vim.F.if_nil(opts.expand_dir, true)
|
|
local git_cmd = git_command({ "status", "-z", "--", "." }, opts)
|
|
|
|
if expand_dir then
|
|
table.insert(git_cmd, #git_cmd - 1, "-u")
|
|
end
|
|
|
|
local output = utils.get_os_command_output(git_cmd, opts.cwd)
|
|
|
|
if #output == 0 then
|
|
utils.notify("builtin.git_status", {
|
|
msg = "No changes found",
|
|
level = "WARN",
|
|
})
|
|
return
|
|
end
|
|
|
|
return finders.new_table {
|
|
results = vim.split(output[1], " |