|
1 | 1 | local M = {} |
| 2 | +local Utils = require("commit-ai.utils") |
| 3 | +local pickers = require("telescope.pickers") |
| 4 | +local Log = require("commit-ai.log") |
| 5 | +local finders = require("telescope.finders") |
| 6 | +local conf = require("telescope.config").values |
| 7 | +local actions = require("telescope.actions") |
| 8 | +local action_state = require("telescope.actions.state") |
| 9 | +local previewers = require("telescope.previewers") |
| 10 | +local telescope = require("telescope.builtin") |
| 11 | +local backends = require("commit-ai.backends.gemini") |
2 | 12 |
|
| 13 | +local function query_ai(prompt, cb) |
| 14 | + backends.call_ai(prompt, function(response) |
| 15 | + -- TODO: need to handle error from gemini |
| 16 | + if response then |
| 17 | + local commit_messages = {} |
| 18 | + if response.candidates and response.candidates[1] and response.candidates[1].content then |
| 19 | + local content = response.candidates[1].content.parts[1].text |
| 20 | + for line in content:gmatch("%- ([^\n]+)") do |
| 21 | + table.insert(commit_messages, line) |
| 22 | + end |
| 23 | + end |
3 | 24 |
|
| 25 | + cb(commit_messages) |
| 26 | + else |
| 27 | + print("No response received") |
| 28 | + cb({}) |
| 29 | + end |
| 30 | + end) |
| 31 | +end |
| 32 | + |
| 33 | +local function generate_commit_suggestions(cb) |
| 34 | + local diff = Utils.get_git_diff() |
| 35 | + if not diff or diff == "" then |
| 36 | + print("No staged changes found") |
| 37 | + return {} |
| 38 | + end |
| 39 | + |
| 40 | + local config = require("commit-ai").config |
| 41 | + local git_conventions = config.git_conventions |
| 42 | + local format_lines = {} |
| 43 | + for _, convention in pairs(git_conventions) do |
| 44 | + table.insert(format_lines, string.format("- %s %s: %s", convention.icon, convention.prefix, convention.type)) |
| 45 | + end |
| 46 | + |
| 47 | + local prompt = string.format( |
| 48 | + "Analyze this git diff and generate commit messages in plain text.\n" .. |
| 49 | + "Use exactly this format without additional explanation:\n\n" .. |
| 50 | + "- <icon> <prefix>: <commit message>\n\n" .. |
| 51 | + "Options:\n%s\n\nGit diff:\n%s", |
| 52 | + table.concat(format_lines, "\n"), |
| 53 | + diff |
| 54 | + ) |
| 55 | + |
| 56 | + -- local prompt = string.format( |
| 57 | + -- "Analyze this git diff and generate multiple commit messages using exactly one of these formats:\n" .. |
| 58 | + -- "Format:\n" .. |
| 59 | + -- "- <icon> <prefix>: <commit message>\n\n" .. |
| 60 | + -- "Only respond with the commit messages, without any explanations.\n\n" .. |
| 61 | + -- "- %s %s: Documentation changes\n" .. |
| 62 | + -- "- %s %s: Bug fix\n" .. |
| 63 | + -- "- %s %s: New feature\n" .. |
| 64 | + -- "- %s %s: Chore\n" .. |
| 65 | + -- "- %s %s: Breaking change\n" .. |
| 66 | + -- "- %s %s: Enhancement\n\n" .. |
| 67 | + -- "Git diff:\n%s", |
| 68 | + -- git_conventions.docs.icon, git_conventions.docs.prefix, |
| 69 | + -- git_conventions.fix.icon, git_conventions.fix.prefix, |
| 70 | + -- git_conventions.feat.icon, git_conventions.feat.prefix, |
| 71 | + -- git_conventions.chore.icon, git_conventions.chore.prefix, |
| 72 | + -- git_conventions.refactor.icon, git_conventions.refactor.prefix, |
| 73 | + -- git_conventions.enhance.icon, git_conventions.enhance.prefix, |
| 74 | + -- diff |
| 75 | + -- ) |
| 76 | + query_ai(prompt, cb) |
| 77 | +end |
| 78 | + |
| 79 | +local diff_previewer = previewers.new_buffer_previewer({ |
| 80 | + title = "Git Diff Preview", |
| 81 | + get_buffer_by_name = function() |
| 82 | + return "Git Diff" |
| 83 | + end, |
| 84 | + define_preview = function(self, entry) |
| 85 | + local diff = Utils.get_git_diff() |
| 86 | + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, vim.split(diff, "\n")) |
| 87 | + vim.api.nvim_buf_set_option(self.state.bufnr, "filetype", "diff") |
| 88 | + end |
| 89 | +}) |
| 90 | + |
| 91 | +-- TODO: lets try to call default_config directly here |
| 92 | +function M.setup(config) |
| 93 | + local default_config = require("commit-ai.config") |
| 94 | + M.config = vim.tbl_deep_extend('force', default_config, config or {}) |
| 95 | +end |
| 96 | + |
| 97 | +function M.commit_picker() |
| 98 | + generate_commit_suggestions(function(commit_suggestions) |
| 99 | + if not commit_suggestions or #commit_suggestions == 0 then |
| 100 | + print("No commit suggestions available") |
| 101 | + return |
| 102 | + end |
| 103 | + |
| 104 | + pickers.new({}, { |
| 105 | + prompt_title = "Select Commit Message", |
| 106 | + finder = finders.new_table({ |
| 107 | + results = commit_suggestions, |
| 108 | + entry_maker = function(entry) |
| 109 | + return { |
| 110 | + value = entry, |
| 111 | + display = entry, |
| 112 | + ordinal = entry, |
| 113 | + } |
| 114 | + end, |
| 115 | + }), |
| 116 | + sorter = conf.generic_sorter({}), |
| 117 | + previewer = diff_previewer, |
| 118 | + attach_mappings = function(prompt_bufnr) |
| 119 | + actions.select_default:replace(function() |
| 120 | + local selection = action_state.get_selected_entry() |
| 121 | + actions.close(prompt_bufnr) |
| 122 | + |
| 123 | + local cmd = string.format('git commit -m "%s"', selection.value) |
| 124 | + -- TODO:as for now we still add the staged changes into one single commit |
| 125 | + -- later it should be possible to add each change into a separate commit |
| 126 | + vim.fn.system('git add .') |
| 127 | + vim.fn.system(cmd) |
| 128 | + print("Committed: " .. selection.value) |
| 129 | + end) |
| 130 | + return true |
| 131 | + end, |
| 132 | + }):find() |
| 133 | + end) |
| 134 | +end |
| 135 | + |
| 136 | +vim.api.nvim_create_user_command("Commit", function() |
| 137 | + M.commit_picker() |
| 138 | +end, {}) |
4 | 139 |
|
5 | 140 | return M |
0 commit comments