fix(parser): detect filetype from existing buffer
Files detected via shebang or modeline (e.g., `build` with `#!/bin/bash`) weren't getting syntax highlighting because `vim.filetype.match()` only does filename-based detection. Now `get_ft_from_filename()` first checks if a buffer already exists for the file and uses its detected filetype. This requires knowing the repo root to construct the full path, so: - `parse_buffer()` reads `b:diffs_repo_root` or `b:git_dir` (fugitive) - `commands.lua` sets `b:diffs_repo_root` on diff buffers it creates Co-authored-by: phanen <phanen@qq.com>
This commit is contained in:
parent
b4e40e4093
commit
c51d625dc6
3 changed files with 98 additions and 2 deletions
|
|
@ -70,6 +70,8 @@ function M.gdiff(revision, vertical)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local repo_root = git.get_repo_root(filepath)
|
||||||
|
|
||||||
local diff_buf = vim.api.nvim_create_buf(false, true)
|
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_buf_set_lines(diff_buf, 0, -1, false, diff_lines)
|
||||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = diff_buf })
|
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('modifiable', false, { buf = diff_buf })
|
||||||
vim.api.nvim_set_option_value('filetype', 'diff', { 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)
|
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.cmd(vertical and 'vsplit' or 'split')
|
||||||
vim.api.nvim_win_set_buf(0, diff_buf)
|
vim.api.nvim_win_set_buf(0, diff_buf)
|
||||||
|
|
@ -154,6 +159,8 @@ function M.gdiff_file(filepath, opts)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local repo_root = git.get_repo_root(filepath)
|
||||||
|
|
||||||
local diff_buf = vim.api.nvim_create_buf(false, true)
|
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_buf_set_lines(diff_buf, 0, -1, false, diff_lines)
|
||||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = diff_buf })
|
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('modifiable', false, { buf = diff_buf })
|
||||||
vim.api.nvim_set_option_value('filetype', 'diff', { 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)
|
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.cmd(opts.vertical and 'vsplit' or 'split')
|
||||||
vim.api.nvim_win_set_buf(0, diff_buf)
|
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('modifiable', false, { buf = diff_buf })
|
||||||
vim.api.nvim_set_option_value('filetype', 'diff', { 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_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.cmd(opts.vertical and 'vsplit' or 'split')
|
||||||
vim.api.nvim_win_set_buf(0, diff_buf)
|
vim.api.nvim_win_set_buf(0, diff_buf)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,21 @@ local M = {}
|
||||||
local dbg = require('diffs.log').dbg
|
local dbg = require('diffs.log').dbg
|
||||||
|
|
||||||
---@param filename string
|
---@param filename string
|
||||||
|
---@param repo_root string?
|
||||||
---@return 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 })
|
local ft = vim.filetype.match({ filename = filename })
|
||||||
if not ft then
|
if not ft then
|
||||||
dbg('no filetype for: %s', filename)
|
dbg('no filetype for: %s', filename)
|
||||||
|
|
@ -39,10 +52,27 @@ local function get_lang_from_ft(ft)
|
||||||
return nil
|
return nil
|
||||||
end
|
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
|
---@param bufnr integer
|
||||||
---@return diffs.Hunk[]
|
---@return diffs.Hunk[]
|
||||||
function M.parse_buffer(bufnr)
|
function M.parse_buffer(bufnr)
|
||||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local repo_root = get_repo_root(bufnr)
|
||||||
---@type diffs.Hunk[]
|
---@type diffs.Hunk[]
|
||||||
local hunks = {}
|
local hunks = {}
|
||||||
|
|
||||||
|
|
@ -95,7 +125,7 @@ function M.parse_buffer(bufnr)
|
||||||
if filename then
|
if filename then
|
||||||
flush_hunk()
|
flush_hunk()
|
||||||
current_filename = filename
|
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
|
current_lang = current_ft and get_lang_from_ft(current_ft) or nil
|
||||||
if current_lang then
|
if current_lang then
|
||||||
dbg('file: %s -> lang: %s', filename, current_lang)
|
dbg('file: %s -> lang: %s', filename, current_lang)
|
||||||
|
|
|
||||||
|
|
@ -304,5 +304,60 @@ describe('parser', function()
|
||||||
assert.are.equal(4, #hunks[1].lines)
|
assert.are.equal(4, #hunks[1].lines)
|
||||||
delete_buffer(bufnr)
|
delete_buffer(bufnr)
|
||||||
end)
|
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)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue