echasnovski / mini.nvim

Library of 40+ independent Lua modules improving overall Neovim (version 0.8 and higher) experience with minimal effort
MIT License
4.45k stars 171 forks source link

Add hooks to define the source per buffer in mini.diff #949

Open nicoe opened 3 weeks ago

nicoe commented 3 weeks ago

Contributing guidelines

Module(s)

mini.diff

Description

I started to use mini.diff and it works great, thanks for all the work!

In my job, I'm using both mercurial and git, so I quickly felt the need to have a source for mercurial. But I didn't found a way to select the right source from the detected VCS managing the file in the buffer.

I ended up creating a source that is handling both but I find it quite ugly. I wonder if it wouldn't be interesting to plug a function somewhere so that the source used by the buffer can be programmatically decided.

For the record here is what I've written (it might be useful to someone) but it's a quick hack and this is nearly my first time writing some lua so I've probably made a lot of mistakes :)

    {
        "echasnovski/mini.diff",
        version=false,
        opts = function(_, opts)
            local MiniDiff = require("mini.diff")
            local git_hg = { name = 'git_hg' }
            local git_source = MiniDiff.gen_source.git()

            git_or_hg = function(path)
                local vcs = { type = nil }
                local cwd = vim.fn.fnamemodify(path, ':h')
                local hg_run = vim.system({ 'hg', 'root' }, { cwd = cwd, stdin = false, stdout = false, stderr = false }):wait()
                local git_run = vim.system({ 'git', 'rev-parse' }, { cwd = cwd, stdin = false, stdout = false, stderr = false }):wait()
                if hg_run.code == 0 then
                    return 'hg'
                elseif git_run.code == 0 then
                    return 'git'
                else
                    return nil
                end
            end

            git_hg.attach = function(buf_id)
                local path = vim.api.nvim_buf_get_name(buf_id)
                if path == '' or vim.fn.filereadable(path) ~= 1 then return false end
                local vcs = git_or_hg(path)
                if vcs == 'git' then
                    git_source.attach(buf_id)
                elseif vcs == 'hg' then
                    local p, stdout_feed = nil, {}
                    local on_exit = function(exit_code)
                        p:close()
                        if exit_code ~= 0 or stdout_feed[1] == nil then
                            vim.schedule_wrap(function()
                                MiniDiff.disable(buf_id)
                            end)()
                            return
                        end
                        vim.schedule_wrap(function()
                            MiniDiff.set_ref_text(buf_id, table.concat(stdout_feed, ''))
                        end)()
                    end
                    local stdout = vim.uv.new_pipe()
                    p = vim.uv.spawn('hg', { args = { 'cat', '--rev', 's0', path }, stdio = { nil, stdout, nil } }, on_exit)

                    local git_read_stream = function(stream, feed)
                        local callback = function(err, data)
                            if data ~= nil then return table.insert(feed, data) end
                            if err then feed[1] = nil end
                            stream:close()
                        end
                        stream:read_start(callback)
                    end
                    git_read_stream(stdout, stdout_feed)
                else
                    vim.schedule_wrap(function()
                        MiniDiff.disable(buf_id)
                    end)()
                end
            end

            git_hg.detach = function(buf_id)
                local path = vim.api.nvim_buf_get_name(buf_id)
                if path == '' or vim.fn.filereadable(path) ~= 1 then return false end
                local vcs = git_or_hg(path)
                if vcs == 'git' then
                    git_source.detach(buf_id)
                end
            end

            return {
                source=git_hg,
            }
        end,
        config = function(_, opts)
            require("mini.diff").setup(opts)
        end,
    },
echasnovski commented 3 weeks ago

Thanks for the suggestion! Glad you're liking 'mini.diff' so far :)

I ended up creating a source that is handling both but I find it quite ugly. I wonder if it wouldn't be interesting to plug a function somewhere so that the source used by the buffer can be programmatically decided.

Yes, this was the intended solution for this kind of problems. I'll take a look at how this can be made easier, but it will probably happen after the next stable 'mini.nvim' release.