diff --git a/lua/diffs/commands.lua b/lua/diffs/commands.lua index 22d0570..0944759 100644 --- a/lua/diffs/commands.lua +++ b/lua/diffs/commands.lua @@ -70,6 +70,8 @@ function M.gdiff(revision, vertical) return end + local repo_root = git.get_repo_root(filepath) + local diff_buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_lines(diff_buf, 0, -1, false, diff_lines) vim.api.nvim_set_option_value('buftype', 'nofile', { buf = diff_buf }) @@ -77,6 +79,9 @@ function M.gdiff(revision, vertical) vim.api.nvim_set_option_value('modifiable', false, { buf = diff_buf }) vim.api.nvim_set_option_value('filetype', 'diff', { buf = diff_buf }) vim.api.nvim_buf_set_name(diff_buf, 'diffs://' .. revision .. ':' .. rel_path) + if repo_root then + vim.api.nvim_buf_set_var(diff_buf, 'diffs_repo_root', repo_root) + end vim.cmd(vertical and 'vsplit' or 'split') vim.api.nvim_win_set_buf(0, diff_buf) @@ -154,6 +159,8 @@ function M.gdiff_file(filepath, opts) return end + local repo_root = git.get_repo_root(filepath) + local diff_buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_lines(diff_buf, 0, -1, false, diff_lines) vim.api.nvim_set_option_value('buftype', 'nofile', { buf = diff_buf }) @@ -161,6 +168,9 @@ function M.gdiff_file(filepath, opts) vim.api.nvim_set_option_value('modifiable', false, { buf = diff_buf }) vim.api.nvim_set_option_value('filetype', 'diff', { buf = diff_buf }) vim.api.nvim_buf_set_name(diff_buf, 'diffs://' .. diff_label .. ':' .. rel_path) + if repo_root then + vim.api.nvim_buf_set_var(diff_buf, 'diffs_repo_root', repo_root) + end vim.cmd(opts.vertical and 'vsplit' or 'split') vim.api.nvim_win_set_buf(0, diff_buf) @@ -205,6 +215,7 @@ function M.gdiff_section(repo_root, opts) vim.api.nvim_set_option_value('modifiable', false, { buf = diff_buf }) vim.api.nvim_set_option_value('filetype', 'diff', { buf = diff_buf }) vim.api.nvim_buf_set_name(diff_buf, 'diffs://' .. diff_label .. ':all') + vim.api.nvim_buf_set_var(diff_buf, 'diffs_repo_root', repo_root) vim.cmd(opts.vertical and 'vsplit' or 'split') vim.api.nvim_win_set_buf(0, diff_buf) diff --git a/lua/diffs/parser.lua b/lua/diffs/parser.lua index da3b291..528327b 100644 --- a/lua/diffs/parser.lua +++ b/lua/diffs/parser.lua @@ -14,8 +14,21 @@ local M = {} local dbg = require('diffs.log').dbg ---@param filename string +---@param repo_root string? ---@return string? -local function get_ft_from_filename(filename) +local function get_ft_from_filename(filename, repo_root) + if repo_root then + local full_path = vim.fs.joinpath(repo_root, filename) + local buf = vim.fn.bufnr(full_path) + if buf ~= -1 then + local ft = vim.api.nvim_get_option_value('filetype', { buf = buf }) + if ft and ft ~= '' then + dbg('filetype from existing buffer %d: %s', buf, ft) + return ft + end + end + end + local ft = vim.filetype.match({ filename = filename }) if not ft then dbg('no filetype for: %s', filename) @@ -39,10 +52,27 @@ local function get_lang_from_ft(ft) return nil end +---@param bufnr integer +---@return string? +local function get_repo_root(bufnr) + local ok, repo_root = pcall(vim.api.nvim_buf_get_var, bufnr, 'diffs_repo_root') + if ok and repo_root then + return repo_root + end + + local ok2, git_dir = pcall(vim.api.nvim_buf_get_var, bufnr, 'git_dir') + if ok2 and git_dir then + return vim.fn.fnamemodify(git_dir, ':h') + end + + return nil +end + ---@param bufnr integer ---@return diffs.Hunk[] function M.parse_buffer(bufnr) local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local repo_root = get_repo_root(bufnr) ---@type diffs.Hunk[] local hunks = {} @@ -95,7 +125,7 @@ function M.parse_buffer(bufnr) if filename then flush_hunk() current_filename = filename - current_ft = get_ft_from_filename(filename) + current_ft = get_ft_from_filename(filename, repo_root) current_lang = current_ft and get_lang_from_ft(current_ft) or nil if current_lang then dbg('file: %s -> lang: %s', filename, current_lang) diff --git a/spec/parser_spec.lua b/spec/parser_spec.lua index 5323d09..e5e1dba 100644 --- a/spec/parser_spec.lua +++ b/spec/parser_spec.lua @@ -304,5 +304,60 @@ describe('parser', function() assert.are.equal(4, #hunks[1].lines) delete_buffer(bufnr) end) + + it('uses filetype from existing buffer when available', function() + local repo_root = '/tmp/test-repo' + local file_path = repo_root .. '/build' + + local file_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(file_buf, file_path) + vim.api.nvim_set_option_value('filetype', 'bash', { buf = file_buf }) + + local diff_buf = create_buffer({ + 'M build', + '@@ -1,2 +1,3 @@', + ' echo "hello"', + '+set -e', + ' echo "done"', + }) + vim.api.nvim_buf_set_var(diff_buf, 'diffs_repo_root', repo_root) + + local hunks = parser.parse_buffer(diff_buf) + + assert.are.equal(1, #hunks) + assert.are.equal('build', hunks[1].filename) + assert.are.equal('bash', hunks[1].ft) + + delete_buffer(file_buf) + delete_buffer(diff_buf) + end) + + it('uses filetype from existing buffer via git_dir', function() + local git_dir = '/tmp/test-repo/.git' + local repo_root = '/tmp/test-repo' + local file_path = repo_root .. '/script' + + local file_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(file_buf, file_path) + vim.api.nvim_set_option_value('filetype', 'python', { buf = file_buf }) + + local diff_buf = create_buffer({ + 'M script', + '@@ -1,2 +1,3 @@', + ' def main():', + '+ print("hi")', + ' pass', + }) + vim.api.nvim_buf_set_var(diff_buf, 'git_dir', git_dir) + + local hunks = parser.parse_buffer(diff_buf) + + assert.are.equal(1, #hunks) + assert.are.equal('script', hunks[1].filename) + assert.are.equal('python', hunks[1].ft) + + delete_buffer(file_buf) + delete_buffer(diff_buf) + end) end) end)