Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add "duplicate" (yank, comment, paste) function? #70

Closed
garymh opened this issue Nov 18, 2021 · 19 comments · Fixed by #73
Closed

Feature: Add "duplicate" (yank, comment, paste) function? #70

garymh opened this issue Nov 18, 2021 · 19 comments · Fixed by #73
Labels
feature New feature

Comments

@garymh
Copy link

garymh commented Nov 18, 2021

Would it be possible to add in a motion for "duplicating" text? I often (hopefully I'm not the only one) will yank and paste and a line/paragraph of code and then comment out the original so I can look at it for reference while I rework it. For example:

def cool_function(hello)
	puts hello
end

Typing something like gcd{motion} (ip here for a paragraph) would give us:

# def cool_function(hello)
# 	puts hello
# end

def cool_function(hello)
	puts hello
end

This was suggested as a feature with vim-commentary but I think it's still being discussed/considered: tpope/vim-commentary#42

It would be neat to see here too 😃

Love the plugin, btw!

@numToStr
Copy link
Owner

numToStr commented Nov 18, 2021

To be clear I won't support this inside the plugin. Currently, I am working on #62 which will give you the selected region in the pre_hook as an argument and you can use pre_hook in a way that is similar to your use case.

I am just dumping my brain here and assuming that the PR is merged. IMO following will be steps

  1. Get the text using the provided range
  2. Add the grabbed text below the original text
  3. The plugin will comment the original text

What do you say?

@numToStr numToStr added the feature New feature label Nov 18, 2021
@garymh
Copy link
Author

garymh commented Nov 18, 2021

@numToStr Sounds great! I appreciate it!

@numToStr
Copy link
Owner

For future somebody, this is what you'll need

require('Comment').setup({
    pre_hook = function(ctx)
        local lines = require('Comment.utils').get_lines(ctx.range)
        local srow = ctx.range.erow
        vim.api.nvim_buf_set_lines(0, srow, srow, false, lines)
    end,
    post_hook = function(ctx)
        local U = require('Comment.utils')
        local srow = ctx.range.erow
        local erow = srow + 1
        local line = U.get_lines({ srow = srow, erow = erow })
        local _, col = U.grab_indent(line[1])
        vim.api.nvim_win_set_cursor(0, { erow, col })
    end,
})

NOTE: The only downside is this duplication will happen every time. But you can determine when to do this by using ctx argument

@garymh
Copy link
Author

garymh commented Dec 20, 2021

Hi @numToStr ! Has this changed recently because of the API? It seems to have stopped working...

@numToStr
Copy link
Owner

numToStr commented Dec 21, 2021

@garymh Yes, because of the recent (breaking) changes I made to the lua APIs. Following is the updated version:

local U = require("Comment.utils")
local A = require("Comment.api")

function _G.___gdc(vmode)
	local range = U.get_region(vmode)
	local lines = U.get_lines(range)

	-- Copying the block
	local srow = range.erow
	vim.api.nvim_buf_set_lines(0, srow, srow, false, lines)

	-- Doing the comment
	A.comment_linewise_op(vmode)

	-- Move the cursor
	local erow = srow + 1
	local line = U.get_lines({ srow = srow, erow = erow })
	local _, col = U.grab_indent(line[1])
	vim.api.nvim_win_set_cursor(0, { erow, col })
end

@garymh
Copy link
Author

garymh commented Dec 22, 2021

Awesome!! Thank you so much!!

@paopaol
Copy link

paopaol commented Apr 24, 2022

how do i use the function ___gdc, :lua _G.__gdc('n') ?

@numToStr
Copy link
Owner

@paopaol See #73 (comment)

@garymh
Copy link
Author

garymh commented Aug 1, 2022

Hello! It looks like the new refactor broke this function...

E5108: Error executing lua ...ite/pack/packer/start/Comment.nvim/lua/Comment/utils.lua:63: attempt to get length of local 'iter' (a nil
 value)                                                                                                                                
stack traceback:                                                                                                                       
        ...ite/pack/packer/start/Comment.nvim/lua/Comment/utils.lua:63: in function 'is_empty'                                         
        ...ite/pack/packer/start/Comment.nvim/lua/Comment/utils.lua:305: in function 'uncomment'                                       
        ...te/pack/packer/start/Comment.nvim/lua/Comment/opfunc.lua:135: in function 'linewise'                                        
        ...te/pack/packer/start/Comment.nvim/lua/Comment/opfunc.lua:76: in function 'opfunc'                                           
        .../site/pack/packer/start/Comment.nvim/lua/Comment/api.lua:152: in function 'comment_linewise_op'                             
        /Users/gary/.config/nvim/lua/config/comment.lua:22: in function </Users/gary/.config/nvim/lua/config/comment.lua:12>    

Should I just stick to 0.6.1? I'm afraid my Lua isn't good enough to fix it 😞

@numToStr
Copy link
Owner

numToStr commented Aug 2, 2022

@garymh This should do it

local U = require('Comment.utils')
local A = require('Comment.api')

function _G.___gdc(motion)
    local range = U.get_region(motion)
    local lines = U.get_lines(range)

    -- Copying the block
    local srow = range.erow
    vim.api.nvim_buf_set_lines(0, srow, srow, false, lines)

    -- Doing the comment
    A.comment_linewise_op(motion)

    -- After #183 you might wanna use
    -- A.comment.linewise(motion)

    -- Move the cursor
    local erow = srow + 1
    local line = U.get_lines({ srow = srow, erow = erow })
    local col = U.indent_len(line[1])
    vim.api.nvim_win_set_cursor(0, { erow, col })
end

(Also make sure to update the plugin, as I discovered a bug while testing this function)

@garymh
Copy link
Author

garymh commented Aug 2, 2022

Perfect! Thank you so much, again! 😄

@AniAggarwal
Copy link

Doesn't seem to work on Neovim v8.
When calling from visual mode:

E5108: Error executing lua /home/ani/.config/nvim/lua/user/comment.lua:20: attempt to call field 'indent_len' (a nil value)                                                 
stack traceback:                                                                                                                                                            
        /home/ani/.config/nvim/lua/user/comment.lua:20: in function '___gdc'                                                                                                
        [string ":lua"]:1: in main chunk   

From normal mode:

E5108: Error executing lua /home/ani/.config/nvim/lua/user/comment.lua:20: attempt to call field 'indent_len' (a nil value)                                                 
stack traceback:                                                                                                                                                            
        /home/ani/.config/nvim/lua/user/comment.lua:20: in function </home/ani/.config/nvim/lua/user/comment.lua:6>  

My mappings/config:

require("Comment").setup({})

local U = require("Comment.utils")
local A = require("Comment.api")

function _G.___gdc(motion)
	local range = U.get_region(motion)
	local lines = U.get_lines(range)

	-- Copying the block
	local srow = range.erow
	vim.api.nvim_buf_set_lines(0, srow, srow, false, lines)

	-- Doing the comment
	A.comment.linewise(motion)

	-- Move the cursor
	local erow = srow + 1
	local line = U.get_lines({ srow = srow, erow = erow })
	local col = U.indent_len(line[1])
	vim.api.nvim_win_set_cursor(0, { erow, col })
end

vim.api.nvim_set_keymap("x", "gy", "<ESC><CMD>lua ___gdc(vim.fn.visualmode())<CR>", { silent = true, noremap = true })
vim.api.nvim_set_keymap("n", "gy", "<CMD>set operatorfunc=v:lua.___gdc<CR>g@", { silent = true, noremap = true })

@hexh250786313
Copy link

My case:

function! s:YankAndComment(type)
  if a:type ==# 'v'
    normal! `<v`>y
  elseif a:type ==# 'V'
    normal! `<V`>y
  elseif a:type ==# 'char'
    normal! `[v`]y
  else
    return
  endif
  lua require('Comment.api').comment.linewise(vim.fn.visualmode())
endfunction

vmap <silent> <space>cy :<c-u>call <SID>YankAndComment(visualmode())<cr>

@AniAggarwal

@AniAggarwal
Copy link

AniAggarwal commented Oct 29, 2022

I am not able to make that work. Unfortunately I am using a lua config. I translated it but don't seem to be able to get it working.

local YankAndComment = function(type)
	if type == "v" then
		vim.cmd("normal! `<v`>y")
	elseif type == "V" then
		vim.cmd("normal! `<V`>y")
	elseif type == "char" then
		vim.cmd("normal! `[v`]y")
	else
		return
	end
	require("Comment.api").comment.linewise(vim.fn.visualmode())
end
keymap("v", "gcp", "<cmd>YankAndComment(vim.fn.visualmode)<cr>", opts)

@hexh250786313

@joncol
Copy link

joncol commented Apr 4, 2023

The solution I went with:

-- Comment and copy.
vim.keymap.set("n", "<leader>cc", "yy<Plug>(comment_toggle_linewise_current)p")
vim.keymap.set(
  "x",
  "<leader>cc",
  "ygv<Plug>(comment_toggle_linewise_visual)`>p"
)

I didn't thoroughly test it yet, but this seems to work with the cursor being at either end of a selection.

@AniAggarwal
Copy link

Works perfectly for me, thanks!

@joncol
Copy link

joncol commented Apr 4, 2023

Works perfectly for me, thanks!

Simplified the binding a little bit.

@tkkcc
Copy link

tkkcc commented Mar 5, 2024

sharing my config that not disturb clipboard and cursor position.

vim.keymap.set('n', ',d', 'mz"zyy"zp`zj', { remap = false, silent=true })
vim.keymap.set('v', ',d', 'mz"zy"zP`z', { remap = false, silent=true })
vim.keymap.set('n', ',D', 'mz"zyy"zP`zk<Plug>(comment_toggle_linewise_current)`z', { remap = false, silent=true })
vim.keymap.set('v', ',D', 'mz"zy"zP`[v`]<Plug>(comment_toggle_linewise_visual)`z', { remap = false, silent=true})

Also, which-key can slow down recursive keymap noticeably.

@powerman
Copy link

powerman commented Jul 8, 2024

sharing my config that not disturb clipboard and cursor position.

Nice! But it does not support [count] and INSERT mode, so here is my version. It's not that good in keeping clipboard and cursor position (maybe someone helps inproving it with this). It's in Lazy keys = format:

            {
                '<C-C>',
                function() -- Support for a count makes implementation a bit complicated.
                    local count = vim.v.count1
                    local api = require 'Comment.api'
                    vim.api.nvim_feedkeys('V', 'n', false)
                    if count > 1 then
                        vim.api.nvim_feedkeys((count - 1) .. 'j', 'n', false)
                    end
                    vim.api.nvim_feedkeys('yP', 'nx', false)
                    api.comment.linewise.count(count)
                    vim.api.nvim_feedkeys('`<^i', 'n', false)
                end,
                mode = 'n',
                desc = 'Backup (count) line(s) as a comment, then edit',
            },
            {
                '<C-C>',
                'V<Esc>gvy`>pgv<Esc>`>:lua require"Comment.api".comment.linewise("V")<CR><Down>^i',
                mode = 'v',
                silent = true,
                desc = 'Backup selected lines as a comment, then edit',
            },
            {
                '<C-C>',
                '<C-O>yy<C-O>P<C-O>:lua require"Comment.api".comment.linewise.current()<CR><Down>',
                mode = 'i',
                silent = true,
                desc = 'Backup current line as a comment',
            },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants