0

These two mappings work separately:

nnoremap <leader>zs :let @+=substitute(substitute(join(filter(getline(1,'$'),'v:val=~"^source:"'),"\n"),'^\(source:\s*\)\(.*\)$','\2',''),'^\s*\(.*\)$','\1','')<CR><ESC>
nnoremap <leader>zd :let @+=getline('.')<CR>

Note on what the codes need to accomplish: the first mapping copies into system clipboard the text following "source: "; the second mapping copies the currently cursored line into system clipboard. I use a clipboard manager (Parcellite) thus I prefer to copy multiple items (separately) from a text file into clipboard.

How do I combine them into a single mapping?

I've tried following this answer to produce:

nnoremap <leader>zs :let @+=substitute(substitute(join(filter(getline(1,'$'),'v:val=~"^source:"'),"\n"),'^\(source:\s*\)\(.*\)$','\2',''),'^\s*\(.*\)$','\1','') \| let @+=getline('.')<CR>

but this doesn't work. What could I be doing wrong?

[UPDATE]

To illustrate, here is a sample text showing [*] where the cursor is currently in:

Her research also shows about 14% of adults are clinically addicted to food, predominantly ultraprocessed foods with higher levels of sugar, salt, fat and additives.

For comparison, 10.5% of Americans age 12 or older were diagnosed with alcohol use disorder in 2022, according to the National Survey on Drug Use and Health. [*]

While many people addicted to food will say that their symptoms began to worsen significantly in adolescence, some recall a childhood focused on ultraprocessed food.

source: https://edition.cnn.com/2024/06/27/health/child-addiction-ultraprocessed-foods-wellness/index.html

After pressing <leader>zs in vim, parcellite should show this when accessed:

what parcellite should show

7
  • Welcome to Vim! What you did seems very correct. But maybe should you tell us what you try to achieve. What would be good is that you give us an buffer example and what you expect from the action induced by the mapping (content of the buffer, content of the registers). It would allow us to makes suggestions about alternative way to solve your problem :-) Commented Jun 27 at 18:25
  • Do you expect that the code generates two entries in the clipboard manager? I fear that running the let @+= will by pass the clipboard manager. Can tell us which is the clipboard manager that you are using such that we can try to reproduce your problem? Commented Jun 27 at 19:23
  • What “doesn’t work”? You run two commands that set the same register, so I would expect the last one to win. Do you want to append two lines of text into the register?
    – D. Ben Knoble
    Commented Jun 27 at 21:43
  • @Vivian De Smedt Thanks. I use parcellite. It allows me to minimize going back and forth several applications by stacking clipboard items. let @+= apparently does not bypass parcellite since the two codes, run separately, achieves the stacking that I intend. I hope this also clarifies @D. Ben Knoble's comment. Commented Jun 28 at 1:36
  • I realize I also need to qualify "it doesn't work". Using what I tried from the link I provided above, clipboard only records the result of the second part of the code. My non-expert suspicion is that there is not enough interval in the sequence between the two process. However, insertiing a :sleep 500m between the two processes prove otherwise. Either that or there is a problem with how is inserted the sleep. Commented Jun 28 at 3:33

1 Answer 1

0

What could I be doing wrong?

Inventing terminology would be one thing: "leader command" and "leader entries" are meaningless, for example.

Focusing on irrelevant details would be another: the "leader" mechanism is totally irrelevant.

And cramming too much functionality into a one-liner would be yet another.

First, let's clean up your original mappings:

nnoremap <leader>zs <Cmd>let @+ =substitute(substitute(join(filter(getline(1,'$'),'v:val=~"^source:"'),"\n"),'^\(source:\s*\)\(.*\)$','\2',''),'^\s*\(.*\)$','\1','')<CR>
nnoremap <leader>zd <Cmd>let @+ =getline('.')<CR>

and make <leader>zs more palatable:

function! GetTextFollowingSource()
    return getline(1, '$')
        \ ->filter('v:val=~"^source:"')
        \ ->join("\n")
        \ ->substitute('^\(source:\s*\)\(.*\)$','\2','')
        \ ->substitute('^\s*\(.*\)$','\1','')
endfunction
nnoremap <leader>zs <Cmd>let @+ = GetTextFollowingSource()<CR>
nnoremap <leader>zd <Cmd>let @+ = getline('.')<CR>

Now, in theory, you should be able to combine them like this:

nmap <key> <leader>zs<leader>zd

or like this:

nnoremap <key> <Cmd>let @+ = GetTextFollowingSource()<CR><Cmd>let @+ = getline('.')<CR>

which should have the same outcome.

They do, actually, but only if you use internal registers:

nnoremap <leader>zs <Cmd>let @a = GetTextFollowingSource()<CR>
nnoremap <leader>zd <Cmd>let @b = getline('.')<CR>

which gives you the "text following source" in register a and the current line in register b. So the problem is elsewhere.

Your assessment that there might be a timing issue can be tested easily outside of your specific mappings with the following command:

:let @+ = 1|let @+ = 2|let @+ = 3

which, in my clipboard manager (not Parcellite), gives me this:

3

which points to only the last invocation having any effect.

Adding delay:

:let @+ = 1|sleep 1|let @+ = 2|sleep 1|let @+ = 3

seems to help, here:

123

Applied to the case at hand:

nmap <key> <leader>zsgs<leader>zd

I get this, which I think is the desired outcome:

success

It also works with the other method:

nnoremap <key> <Cmd>let @+ = GetTextFollowingSource()<CR>gs<Cmd>let @+ = getline('.')<CR>

Now, which method to choose?

The first one is short and sweet, but:

  • the three parts of the RHS (<leader>zs, gs, and <leader>zd) are not clearly delimited, which impacts readability,
  • the value of :help mapleader can be changed between two <leader> expansions so this <leader> might not be the one used for the original mappings.

The second one is easier to follow and immune to <leader> shadowing, but it is starting to become too long for my taste. YMMV.

If you want to keep the original individual mappings, then both methods are fine.

If you don't care about the original individual mappings, then the second method might be better but, at this point, why not use a single easy-to-follow function?

function! ExtractLineAndSource()
    let @+ = getline(1, '$')
        \ ->filter('v:val=~"^source:"')
        \ ->join("\n")
        \ ->substitute('^\(source:\s*\)\(.*\)$','\2','')
        \ ->substitute('^\s*\(.*\)$','\1','')
    sleep
    let @+ = getline('.')
endfunction
nnoremap <key> <Cmd>call ExtractLineAndSource()<CR>
4
  • @romani, I really appreciate your highly didactic reply, criticisms included and humbly accepted. I used both methods you provided, using instead the key combinations z1 and z2 to replace the previous zs, zd, I used z3 for the second one you provided. However, the same predicament befalls me, i.e., having only the result of the code's latter procedure in the clipboard stack. As it worked in you non-parcellite application, the problem is now probably due to Parcellite's internal workings, though this again is speculation. Commented Jun 28 at 10:10
  • @JitteryNutmeg if it's a timing thing, you may try to ramp up the sleep calls.
    – Friedrich
    Commented Jun 28 at 10:13
  • Thanks, @Friedrich. 3gs may be too long a wait, but it does the job. Commented Jun 28 at 10:28
  • 1
    Note that you can use <Cmd>sleep 1500m<CR> instead of gs, which gives millisecond-level precision.
    – romainl
    Commented Jun 28 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.