Is there a way to operate macro to all markers? - vim

For example if a use ma mb etc to create some markers in buffer. Then i record a macro, then i want to execute macro to all these markers, how can i do to accomplish this goal
Maybe it can be completed by writing a function by lua or viml, or use some plugins or just a vim command is all accepted. I'd like someone can give a example function to make me learn more about neovim or vim

You can use getpos("'".mark_name) to obtain the position of a mark. And a macro can be executed with exe 'normal #'.macro_name.
Which gives:
function! s:exec(macro, marks) abort
for mark in split(a:marks, '\zs\ze')
call setpos('.', getpos("'".mark))
exe 'normal #'.a:macro
endfor
endfunction
command! -nargs=+ RunMacroOnMarks call s:exec(<f-args>)
Then if you've recorded a macro in register m and marked two positions a and b, it can be used with:
:RunMacroOnMarks m ab
Note: I've chosen to be explicit with the list of marks as we are likely to want to restrict the macro to a very limited number of registered positions.

I use marks.nvim to accomplish this.
then set keymap
local keymap = vim.api.nvim_set_keymap
keymap('n', '<leader>xx', '<Plug>(Marks-next)', { noremap = true, silent = true })
now you can combo with macro.

Related

paste in "vimL normal execution" the string located in a register memory

How do I execute a command which involves a register data inside a vimL function?
say (as an example test case) I want to open a file whose direction string is in register z. During normal execution I could hit the keys:
:e <c-r>z<CR> # CR is "carret return", <c-r>z is "paste registry z"
In vimL it gets complicated. I tried the following options and are not working.
fun! OpenFileInBrackets()
exe 'normal! :e \<c-r>z'."\<cr>"
exe 'normal! :e <c-r>z'."\<cr>"
norm! :e <c-r>z<CR>
endfun
What are the options?
thank you!
Moving my comment to an answer.
My suggestion would be to use functions inside of vim scripts rather than their normal mode equivalents. In this case, to get the value from a register, use getreg().
Here's a quick example:
function OpenRegZ()
let filename = getreg("z")
execute "edit " . fnameescape(filename)
endfunction
A list of available functions can be found at :help function-list.
Edit: Added fnameescape() per the suggestion of :help execute.

Is it possible to change the Vim background color after a certain line in the file (`private` in a Ruby class definition)?

I would love to be able to see in Vim whether I'm in the "private" section of a Ruby class definition or not. Class definitions look like this:
class Foo
def a_public_method
end
private
def a_private_method
end
end
Would it be possible to get Vim to change the background color of the private line and everything after?
A possible solution is:
:call matchadd('Error', '^.*private\_.\{-}\(^end\)\#=')
assuming the end closing class starts always at the beginning of a line (not a very elegant solution).
You can use another highlight group listed by :hi instead of 'Error'.
I've been trying to mark private methods in ruby for years, but I always start and stop, and, now, I finally have a reasonably-working idea, thanks to your question. So, thanks for that.
Now, first off, the ideal solution would use Vim's built-in highlighting to extend ruby's syntax file. The way I'd imagine this would work is by defining a syntax region (:help syn-region) that starts with a "private" and ends with an "end". The tricky bit is that the "private" needs to have been marked with a particular syntax group, rubyAccess, or it might just be any "private" in a string, or comment. And the end needs to be rubyClass, or it might be closing not a class, but a method's def or a do.
Unfortunately, I couldn't figure out how to pull this off. I can't find a way to define a syntax region from a particular group to another one. If you want to study syntax highlighting, and maybe someday come up with a solution to the problem, here's ruby's syntax highlighting: https://github.com/vim-ruby/vim-ruby/blob/f792ee3b2d005660da2e7303e43721ef222d7047/syntax/ruby.vim
Now, that said, after I defined the problem in the terms above, I realized I could just find the relevant "private" and "end" keywords, with the right syntax groups, upon saving the buffer or something. Considering you're thinking of an explicit command anyway, this might be a feasible solution to you as well.
I have a gist here, if you find that more readable: https://gist.github.com/AndrewRadev/d848ad9621b47b4151bbc61ab6c5765f. It needs to go in ~/.vim/ftplugin/ruby.vim. Here it is, annotated with some comments:
" Define what color the private area will be
hi rubyPrivateArea ctermbg=darkgray
function! s:MarkPrivateArea()
" Clear out any previous matches
call clearmatches()
" Store the current view, in order to restore it later
let saved_view = winsaveview()
" start at the last char in the file and wrap for the
" first search to find match at start of file
normal! G$
let flags = "w"
while search('\<private\>', flags) > 0
let flags = "W"
if s:CurrentSyntaxName() !~# "rubyAccess"
" it's not a real access modifier, keep going
continue
endif
let start_line = line('.')
" look for the matching "end"
let saved_position = getpos('.')
while search('\<end\>', 'W') > 0
if s:CurrentSyntaxName() !~# "rubyClass"
" it's not an end that closes a class, keep going
continue
endif
let end_line = line('.') - 1
call matchadd('rubyPrivateArea', '\%'.start_line.'l\_.*\%'.end_line.'l')
break
endwhile
" restore where we were before we started looking for the "end"
call setpos('.', saved_position)
endwhile
" We're done highlighting, restore the view to what it was
call winrestview(saved_view)
endfunction
function! s:CurrentSyntaxName()
return synIDattr(synID(line("."), col("."), 0), "name")
endfunction
augroup rubyPrivateArea
autocmd!
" Initial marking
autocmd BufEnter <buffer> call <SID>MarkPrivateArea()
" Mark upon writing
autocmd BufWrite <buffer> call <SID>MarkPrivateArea()
" Mark when not moving the cursor for 'timeoutlen' time
autocmd CursorHold <buffer> call <SID>MarkPrivateArea()
augroup END
At a high level: It defines a function, s:MarkPrivateArea that performs the actual searching and marking of the area. The s: indicates it's script-local, so it doesn't pollute the global namespace. It's later called in autocommands as <SID>MarkPrivateArea -- it's the same function.
The function starts from the end of the file, loops around, and then forbids looping, letting it cover the entirety of the file, only once. It looks for "private" with the right syntax group, finds the very next "end" that closes a class, and saves the start_line and end_line for that region. You can play around with adding or removing - 1 in order to decide if your "region" should include or exclude the "private" and whether it should include or exclude the final "end".
(Side note: for this to work, you need to have "expensive" syntax highlighting, but that should be on by default. Relevant docs: https://github.com/vim-ruby/vim-ruby/blob/f792ee3b2d005660da2e7303e43721ef222d7047/doc/ft-ruby-syntax.txt#L71)
In the end, the start and end lines are combined into a pattern that looks like this:
'\%<start-line>l\_.*\%<end-line>.'l'
Try :help \%l for more info, but that's basically a regex pattern that matches code in a particular line of the file. And \_. is the multiline form of .. So, it matches everything from the start line to the end line.
If you wanted to change that so that only the defs got highlighted, it would look like this:
call matchadd('rubyPrivateArea', '\%>'.start_line.'l\<def\>\%<'.end_line.'l')
That's what I'll be doing, at least, but you did say you wanted the entire area marked.
The autocommands at the end run the function on startup, upon writing the file, and upon holding the cursor for some time. You could remove some of these, if you like. For instance, you might be okay even without the CursorHold one, if you save often.
As for the rubyPrivateArea highlight group at the top of the file, there's a full explanation on how you can set its colors with :help highlight-args. You could also take a look at your favorite colorscheme for some examples.

Is it possible to record and run recursive macro's with Vim's normal command?

Given:
https://stackoverflow.com/questions/ask
From normal mode at the first character, typing in qaqqaf/xb#aq#a clears all of the forward slashes.
qaq clears the a register
qa starts recording to a
f/x deletes the next forward slash
#a re-runs the macro
q ends the recording
But running normal qaqqaf/xb#aq#a stops after b -- it seems to bail at the recursive call. The same happens if you try to use map the command.
Is there something wrong with my syntax? Or is it impossible to record a recursive macro with normal?
Note: I know it's possible to write a recursive macro with let. I'm wondering if this is the only way to write a recursive macro without recording it manually:
let #a = "f/xb#a"
normal #a
(I ask because of this answer: Remove everything except regex match in Vim )
If you want to create a map to a recursive macro I suggest you start by doing something like so:
nmap <f2> :let #a = "f/xb#a"|normal #a
Of course this clobbers the #a register and if you find you self doing many of these kinds of mappings maybe a function would better suit your needs.
Here is a safer alternative to making recursive macro mappings:
function! RecMacroExe(cmds)
let a = #a
let #a = a:cmds . "#a"
try
normal #a
finally
let #a = a
endtry
endfunction
nmap <f2> :call RecMacroExe("f/xb")<cr>
Edit: Changed function according to #Luc Hermitte comment

How to detect a function in Vim?

If the cursor is in somewhere within a very long function, is there a way to let Vim tell the user in which function he/she is editing?
By the way, I use taglist but seems that taglist does not auto update where you are even if you have moved the cursor to a different function.
The taglist plugin provides this feature. The function in which
the cursor is currently positioned is highlighted automatically in the list of
functions of taglist.
Make sure that Tlist_Auto_Highlight_Tag is not equal 0 to enable this feature.
'updatetime' defines the time of no activity which must elapse before
taglist highlights the current function. Default is 4 seconds.
:help taglist.txt See section "Highlighting the current tag"
As a quick test:
Type :TlistHighlightTag to force taglist to highlight the current function.
If this works I suppose that you have disabled the automatic highlighting in any
way (see Tlist_Auto_Highlight_Tag).
As an addition to Habi's answer, if you want to do it without using taglist, you can quite easily define a function that will work it out. It depends what language you're programming in, but for C-like languages, you can do this:
nmap ,f call ShowFuncName()
" Show the name of the current function (designed for C/C++, Perl, Java etc)
fun! ShowFuncName()
let lnum = line(".")
let col = col(".")
echohl ModeMsg
echo getline(search("^[^ \t#/]\\{2}.*[^:]\s*$", 'bW'))
echohl None
call search("\\%" . lnum . "l" . "\\%" . col . "c")
endfun
Put that it in your vimrc and then press ,f to see the current function.
Taken from here.

Reading the result of an exe file in :vs in vim

Many of my programs are console like applications, which take a data file, and print out the results, either on screen (more often) or in another file.
Since these days I'm doing some analysis which requires a lot of little tempting with one input data file, just to get a few numbers, I usually go: edit data file, start program, get result, edit data file, start program, get result ...
So I was wondering, is there a way in vim, while having open the input file, to define a function which would open a vertical split and load the result of program (pro12.exe) in it ?
How would one go about that ?
The easiest thing to do is probably write a Makefile to run the program (and redirect output to a file), and :set the autoread option on in Vim. Then, you can create a macro r by typing qr:make<Enter>q and run the macro with #r. The macro will cause Vim to invoke make, which will run the program to update the data file. The autoread option will make sure that vim refreshes the updated file without prompting you first.
Assuming pro12.exe is in your %PATH%, this will invoke your helper app and vert-split-open a static output file name. Map to whatever key you like. QND: won't work when relative paths (bufnames) change via cd. YMMV
fun! RunPro12()
bufdo if bufname(bufnr($)) == '/path/to/pro12.exe.output' | close | endif
silent exe '!pro12.exe'
vs /path/to/pro12.exe.output
endfun
map <f3> :call RunPro12()<cr>
I don't like :bufdo solutions as this command always messes up the current windows organisation.
In normal time, I'd use lh#buffer#jump() that jumps to the window where the named buffer is opened if it is already opened, or opens the window otherwise.
function! s:Run0()
call lh#buffer#jump('/path/to/pro12.exe.output', 'vsp')
silent exe '!ls'
endfunction
Given the requested "to define a function which would open a vertical split", we could simply use:
function! s:Run1()
let bn = bufnr('/path/to/pro12.exe.output')
if bn >= 0
exe 'bw '.bn
endif
silent exe '!pro12'
vsp /path/to/pro12.exe.output
endfunction
nnoremap ยต :call <sid>Run1()<cr>
Last thing. If as I suspect pro12 writes to stdout (the standard output, i.e. ~ the console), the last two lines of the function shall become:
vsp /path/to/pro12.exe.output
r!pro12
The "/path/to/pro12.exe.output" may be replaced with anything unique. Typically, I'd use a scratch buffer with a unique name having "pro12 result" in it.
PS: if in the function (this is important) you add a
nnoremap <buffer> q :bw<cr>
you'll be able to simply quit the output window simply by hitting q, from within the output window.

Resources