diff --git a/lua/telescope/actions/init.lua b/lua/telescope/actions/init.lua index 4bc230a..c6f2f25 100644 --- a/lua/telescope/actions/init.lua +++ b/lua/telescope/actions/init.lua @@ -189,9 +189,22 @@ actions.insert_value = function(prompt_bufnr) return entry.value end +actions.git_checkout = function(prompt_bufnr) + local selection = actions.get_selected_entry(prompt_bufnr) + actions.close(prompt_bufnr) + local val = selection.value + os.execute('git checkout ' .. val) +end + +actions.git_add = function(prompt_bufnr) + local selection = actions.get_selected_entry(prompt_bufnr) + actions.close(prompt_bufnr) + local val = selection.value + os.execute('git add ' .. val) +end + -- ================================================== -- Transforms modules and sets the corect metatables. -- ================================================== actions = transform_mod(actions) return actions - diff --git a/lua/telescope/builtin/git.lua b/lua/telescope/builtin/git.lua new file mode 100644 index 0000000..0f90403 --- /dev/null +++ b/lua/telescope/builtin/git.lua @@ -0,0 +1,177 @@ +local actions = require('telescope.actions') +local finders = require('telescope.finders') +local make_entry = require('telescope.make_entry') +local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') +local utils = require('telescope.utils') + +local conf = require('telescope.config').values + +local git = {} + +git.files = function(opts) + local show_untracked = utils.get_default(opts.show_untracked, true) + + -- By creating the entry maker after the cwd options, + -- we ensure the maker uses the cwd options when being created. + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + pickers.new(opts, { + prompt_title = 'Git File', + finder = finders.new_oneshot_job( + { "git", "ls-files", "--exclude-standard", "--cached", show_untracked and "--others" }, + opts + ), + previewer = previewers.cat.new(opts), + sorter = conf.file_sorter(opts), + }):find() +end + +git.commits = function(opts) + local cmd = 'git log --pretty=oneline --abbrev-commit' + local results = vim.split(utils.get_os_command_output(cmd), '\n') + + pickers.new(opts, { + prompt_title = 'Git Commits', + finder = finders.new_table { + results = results, + entry_maker = make_entry.gen_from_git_commits(opts), + }, + previewer = previewers.git_commit_diff.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.goto_file_selection_edit:replace(actions.git_checkout) + return true + end + }):find() +end + +git.bcommits = function(opts) + local cmd = 'git log --pretty=oneline --abbrev-commit ' .. vim.fn.expand('%') + local results = vim.split(utils.get_os_command_output(cmd), '\n') + + pickers.new(opts, { + prompt_title = 'Git BCommits', + finder = finders.new_table { + results = results, + entry_maker = make_entry.gen_from_git_commits(opts), + }, + previewer = previewers.git_commit_diff.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.goto_file_selection_edit:replace(actions.git_checkout) + return true + end + }):find() +end + +git.branches = function(opts) + -- Does this command in lua (hopefully): + -- 'git branch --all | grep -v HEAD | sed "s/.* //;s#remotes/[^/]*/##" | sort -u' + local output = vim.split(utils.get_os_command_output('git branch --all'), '\n') + + local tmp_results = {} + for _, v in ipairs(output) do + if not string.match(v, 'HEAD') and v ~= '' then + v = string.gsub(v, '.* ', '') + v = string.gsub(v, '^remotes/.*/', '') + tmp_results[v] = true + end + end + + local results = {} + for k, _ in pairs(tmp_results) do + table.insert(results, k) + end + + pickers.new(opts, { + prompt_title = 'Git Branches', + finder = finders.new_table { + results = results, + entry_maker = function(entry) + return { + value = entry, + ordinal = entry, + display = entry, + } + end + }, + previewer = previewers.git_branch_log.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.goto_file_selection_edit:replace(actions.git_checkout) + return true + end + }):find() +end + +git.status = function(opts) + local output = vim.split(utils.get_os_command_output('git status -s'), '\n') + local results = {} + for _, v in ipairs(output) do + if v ~= "" then + local mod, fname = string.match(v, '(..)%s(.+)') + if mod ~= 'A ' and mod ~= 'M ' and mod ~= 'R ' and mod ~= 'D ' then + table.insert(results, { mod = mod, file = fname }) + end + end + end + + if vim.tbl_isempty(results) then + print('No changes found') + return + end + + pickers.new(opts, { + prompt_title = 'Git Status', + finder = finders.new_table { + results = results, + entry_maker = function(entry) + return { + value = entry.file, + ordinal = entry.mod .. ' ' .. entry.file, + display = entry.mod .. ' ' .. entry.file, + } + end + }, + previewer = previewers.git_file_diff.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.goto_file_selection_edit:replace(actions.git_add) + return true + end + }):find() +end + +local set_opts_cwd = function(opts) + local is_git_dir = function(path) + vim.fn.system('cd ' .. path .. ' && git rev-parse HEAD >/dev/null 2>&1') + if vim.v.shell_error ~= 0 then + error(path .. ' is not a git directory') + end + end + + if opts.cwd then + opts.cwd = vim.fn.expand(opts.cwd) + is_git_dir(opts) + else + is_git_dir(vim.fn.expand('%:p:h')) + --- Find root of git directory and remove trailing newline characters + opts.cwd = vim.fn.systemlist("git rev-parse --show-toplevel")[1] + end +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + set_opts_cwd(opts) + v(opts) + end + end + + return mod +end + +return apply_checks(git) diff --git a/lua/telescope/builtin.lua b/lua/telescope/builtin/init.lua similarity index 96% rename from lua/telescope/builtin.lua rename to lua/telescope/builtin/init.lua index 4102906..2336a42 100644 --- a/lua/telescope/builtin.lua +++ b/lua/telescope/builtin/init.lua @@ -39,39 +39,13 @@ local conf = require('telescope.config').values local filter = vim.tbl_filter local flatten = vim.tbl_flatten - local builtin = {} -builtin.git_files = function(opts) - opts = opts or {} - - local show_untracked = utils.get_default(opts.show_untracked, true) - - if opts.cwd then - opts.cwd = vim.fn.expand(opts.cwd) - else - --- Find root of git directory and remove trailing newline characters - opts.cwd = vim.fn.systemlist("git rev-parse --show-toplevel")[1] - - if 1 ~= vim.fn.isdirectory(opts.cwd) then - error("Not a working directory for git_files:" .. opts.cwd) - end - end - - -- By creating the entry maker after the cwd options, - -- we ensure the maker uses the cwd options when being created. - opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) - - pickers.new(opts, { - prompt_title = 'Git File', - finder = finders.new_oneshot_job( - { "git", "ls-files", "--exclude-standard", "--cached", show_untracked and "--others" }, - opts - ), - previewer = previewers.cat.new(opts), - sorter = conf.file_sorter(opts), - }):find() -end +builtin.git_files = require('telescope.builtin.git').files +builtin.git_commits = require('telescope.builtin.git').commits +builtin.git_bcommits = require('telescope.builtin.git').bcommits +builtin.git_branches = require('telescope.builtin.git').branches +builtin.git_status = require('telescope.builtin.git').status builtin.commands = function() pickers.new({}, { diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index 5e7d493..906eb95 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -134,7 +134,7 @@ do end local execute_keys = { - path = function(t) + path = function(t) return t.cwd .. path.separator .. t.filename, false end, @@ -215,6 +215,24 @@ do end end +function make_entry.gen_from_git_commits(opts) + opts = opts or {} + + return function(entry) + if entry == "" then + return nil + end + + local sha, msg = string.match(entry, '([^ ]+) (.+)') + + return { + value = sha, + ordinal = sha .. ' ' .. msg, + display = sha .. ' ' .. msg, + } + end +end + function make_entry.gen_from_quickfix(opts) opts = opts or {} opts.tail_path = get_default(opts.tail_path, true) diff --git a/lua/telescope/previewers.lua b/lua/telescope/previewers.lua index f9d8e5b..d85d149 100644 --- a/lua/telescope/previewers.lua +++ b/lua/telescope/previewers.lua @@ -338,6 +338,32 @@ previewers.vim_buffer = defaulter(function(_) } end, {}) +previewers.git_commit_diff = defaulter(function(_) + return previewers.new_termopen_previewer { + get_command = function(entry) + local sha = entry.value + return { 'git', '--no-pager', 'diff', sha .. '^!' } + end + } +end, {}) + +previewers.git_branch_log = defaulter(function(_) + return previewers.new_termopen_previewer { + get_command = function(entry) + return { 'git', 'log', '--graph', + '--pretty=format:%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset', + '--abbrev-commit', '--date=relative', entry.value } + end + } +end, {}) + +previewers.git_file_diff = defaulter(function(_) + return previewers.new_termopen_previewer { + get_command = function(entry) + return { 'git', '--no-pager', 'diff', entry.value } + end + } +end, {}) previewers.cat = defaulter(function(opts) local maker = get_maker(opts)