duplicate.nvim icon indicating copy to clipboard operation
duplicate.nvim copied to clipboard

Discussion: smart duplications (transform function) + presets?

Open chrisgrieser opened this issue 2 years ago • 3 comments

I basically have a mini-plugin in my config which tries to improve current-line-duplication by adding some minor tweaks like:

  • in css, when a line with "margin-right" is duplicated, it becomes "margin-left" etc.
  • if the line contains a = or :, move the cursor to the left-hand-side, since that's the part you are most likely to change (since duplicate keys are invalid, and you usually do not want to assign something to the very same variable)
  • if the left-hand-side contains a numbered key or variable, increment that number
  • ...

I think those ideas could make sense as an (optional) extra feature for this plugin. You can take a look at what I have here: https://github.com/chrisgrieser/.config/blob/main/nvim/lua/funcs/duplication.lua

chrisgrieser avatar Jan 19 '23 22:01 chrisgrieser

That's a really great feature request! On v2.0.0 (btw this plugin now uses semver and there were breaking changes to the configuration, see readme), I added basic support for this. You can now do something like this:

    require("duplicate").setup {
      transform = function(lines)
        local ft = vim.bo.filetype
        for i, line in ipairs(lines) do
          -- smart switching of conditionals
          if ft == "lua" and line:find("^%s*if.+then$") then
            lines[i] = line:gsub("^(%s*)if", "%1elseif")
          elseif (ft == "bash" or ft == "zsh" or ft == "sh") and line:find("^%s*if.+then$") then
            lines[i] = line:gsub("^(%s*)if", "%1elif")
          elseif (ft == "javascript" or ft == "typescript") and line:find("^%s*if.+{$") then
            lines[i] = line:gsub("^(%s*)if", "%1} else if")
          end

          -- increment numbered vars
          local lineHasNumberedVarAssignment, _, num = line:find("(%d+).*=")
          if lineHasNumberedVarAssignment then
            local nextNum = tostring(tonumber(num) + 1)
            lines[i] = line:gsub("%d+(.*=)", nextNum .. "%1")
          end
        end
        return lines
      end,
    }

Let me know if you think I should add presets for this as you mentioned (e.g. you could then do smth. like this instead of returning the lines:

transform = function(lines)
  return {
    presets = {
      lua = { "if/elseif" } -- enable presets specified by name
      css = "all", -- use all available presets
    }
    -- or just `presets = "all"`
  }
end

smjonas avatar Jan 21 '23 13:01 smjonas

fantastic, thanks! 🚀

(btw this plugin now uses semver and there were breaking changes to the configuration, see readme),

all good, lazy.nvim dutifully informed me that there is a breaking change :)


a few points of feedback on the implementation

  • I think it makes sense to restrict the transformation to the current line. the ifelseif transformation for example makes sense if duplicating a single line, but when duplicating a whole block or paragraph, you usually want to keep the if, for example. (To make transformations work with more than one line, one would probably need some smarter conditionals, although I am not sure what a good implementation for that would be.)
  • presets definitely would make sense! I'd separate the presets from the custom transform function though, in case someone would like to use both.

chrisgrieser avatar Jan 21 '23 14:01 chrisgrieser

I think it makes sense to restrict the transformation to the current line.

I think I'll leave this as it is since the feature is more general this way (in the transform function you could add a check for the size of lines).

presets definitely would make sense!

After having thought about adding presets a bit more I'd say that it's not obvious to me what some common use cases / examples for transformation functions would be (since they depend a lot on the filetype / context). If it turns out that several users extensively use the same transformation functions I can still add this. But right now I don't think it'd be worth the effort given the low amount of users rn (also because I haven't announced this plugin on Reddit yet ;))

Will leave this issue open for further discussion.

smjonas avatar Jan 29 '23 21:01 smjonas