Vim's custom foldmarker {,} doesn't work - vim

I have a js file with following content:
function do_this(){
a = '{1}';
}
function do_that(a){
b = b + 1;
}
// vim: set fdm=marker fmr={,} :
When it folds it shows following:
function do_this(){
a = '{1}';
}
function do_that(a){ +-- 3 lines_____________
// vim: set fdm=marker fmr={,} :
I expect both functions to be folded. I guess "a = '{1}';" is getting in the way.
Is there a way to fix this using only the custom marker "{,}" within the modeline?

Unfortunately foldmarker does not allow regex matching, as specified by :h fmr. Therefore it will only match a literal string, so there's no way getting around the a = '{1}' in your example. However, it seems like what you really want is
// vim: set fdm=syntax fdls=1 :
with
let javaScript_fold=1
in your ~/.vimrc.

Related

Selecting a Javascript arrow function in Vim

It's been a challenge for me to select a whole arrow function via Vim without using relative line numbers. Any thoughts on how I can accomplish that?
Here is an example:
const myFunction = () => {
console.log('hello')
}
As always in Vim, there are lots of ways to do that but the right way is very context-dependent. For example, this problem:
const myFunction = () => {
console.log('hello')
}
is different from this problem:
console.log('---')
const myFunction = () => {
console.log('hello')
}
console.log('---')
or this one:
console.log('---')
const myFunction = () => {
console.log('hello')
}
console.log('---')
and the location of the cursor might be important as well.
Assuming your cursor is somewhere on the first or last line of the snippet, the following command should do what you want:
$V% " move cursor to end of line,
" enter visual-line mode,
" extend the selection to the matching pair
If the cursor is somewhere in the body, you could simply do Vip to visually select the current paragraph but that won't work if there are empty lines in the body or if you have other lines above and below. This is why context matters.
With the cursor somewhere in the body, you could do:
[mV% " jump to previous start of method,
" enter visual-line mode,
" extend the selection to the matching pair
But there are various ways in which it might not work, like when you are in a conditional or a switch.
FWIW, I have used (and updated) the following snippet for many years because the built-in ways have always felt limited to me:
" in after/ftplugin/javascript.vim
function! SelectFunction() abort
call search('\(function\|=>\)', "bWc")
call search("{", "Wc")
normal v%V
endfunction
xnoremap <buffer> af :<C-u>call SelectFunction()<CR>
onoremap <buffer> af :normal vaf<CR>
SelectFunction() searches for a typical JS function declaration at or before the cursor, then it searches for the opening brace at or after the cursor, then it visually selects until the matched pair.
The two mappings essentially define a custom pseudo-text object af that uses SelectFunction() under the hood. It is not a real text object in the sense that it doesn't support count or nesting, but it is nevertheless pretty damn useful:
vaf " visually select current function
daf " cut current function
yaf " yank current function
" etc.
vaf with the cursor somewhere in the body, then daf with the cursor on the closing brace:
Note that the function above probably has shortcomings. From previous interactions with the community, it seems that "select the current function" is not exactly a solved problem.

Vim Quickfix prefixes double-bar "||" — explain?

I use the Quickfix view in Vim often.
The text in there always has a prefix of || added to it.
So, for instance, when I copy/paste out of that buffer, etc. I get those characters included by default.
Is there a way to disable this?
I haven't had luck finding any documentation or configuration for this...
Quickfix buffer is supposed to be used for parsing specially formatted strings (like compiler messages). This is done with the help of :h 'errorformat' option. And those "bars" are output separators between "filename", "line number" and "the message body".
If you have only "double bars" at the beginning of a line then you either have errorformat set wrong, or you misuse the quickfix buffer.
UPD. If you're interested, "Bars" are hardcoded in Vim's source (src/quickfix.c):
static int
qf_buf_add_line(buf_T *buf, linenr_T lnum, qfline_T *qfp, char_u *dirname)
{
...
if (qfp->qf_module != NULL)
...
if (len < IOSIZE - 1)
IObuff[len++] = '|';
if (qfp->qf_lnum > 0)
...
if (len < IOSIZE - 2)
{
IObuff[len++] = '|';
IObuff[len++] = ' ';
}
...
}
It is now possible to customize the display of the quickfix window.
vim has introduced the quickfixtextfunc (:h qftf).
It allows exactly to customize the rendering of the quickfix window. The documentation includes an example, you can also see an example in the nvim-bqf README, although it's neovim/lua based.
You can see an example in the vim documentation in :h quickfix-window-function.
To implement a general-purpose qftf (not a specific one as in the vim documentation), you should start similarly than in the nvim-bqf readme, meaning, check if the info parameter quickfix field is 1, you should display items from getqflist, otherwise items from getloclist

vim: set fix jump marker in a source comment of the current file

I'm a heavy VIM user but I missing one thing…
setting a fix jump marker in a comment on different parts of a (large) current open file
What I expect:
setting a marker in a comment like: // vim: marker(x)
jumping to the marker with: 'x
example (using tcl code) using 'x to jump to function p_structDEF_MqC
proc p_func_arg_name_DEF_MqC {key value} {
set cmd "N"
regexp {^(\w)#(.*)} $value dummy cmd value
set ::ARG_DEFAULT(k,$key) $cmd
set ::ARG_DEFAULT(v,$key) $value
}
proc p_arg_name_DEF_MqC {key value} {
func_arg_name_DEF_${::LANG}_MqC $key $value
}
## vim: marker(x)
proc p_structDEF_MqC {name} {
global xCLASS xPREFIX
set prefix [string range $name 0 end-1]
if {$prefix ne "Mq"} {
lappend xPREFIX "$prefix"
}
lappend xCLASS "${prefix}C"
}
proc p_enumDEF_MqC {name argv} {
}
proc p_typeDEF_MqC {VAR VALUE} {
}
proc p_fupuDEF_MqC {name ret argv} {
}
You can create an autocommand that scans files for those marks,
and create them using setpos(). You can go as complex as
you want in your scripting, i.e. to analyze the next line
and determine where the mark should be. Here is a simple
implementation in a single command that creates marks on the
first character of the next line:
au BufRead * g/vim: marker([a-z])/call setpos(
\ "'".matchstr(getline('.'), '(\zs\w'),
\ [0, getpos('.')[1]+1, 1, 0] )
This answer is based mainly on :autocmd and :g. You should
check the help files for both (:h :au and :h :g). An
autocommand runs the specified command when a certain event
happens for files matching a specific pattern. So, more help
topics for you to read:
autocmd-events
autocmd-patterns
You can use the pattern to restrict this to certain files.
Then, we have the :g command which searches for lines matching
a pattern and executes a command. The search is where you modify
which flags are you looking for. Based on your comment, let's say
for example you want to make the space after : optional. Then
update your regex to:
vim: *marker([a-z])
Modify this as you need.
The command is centered in the setpos() (again search the help
for it... everything I'm saying is in the help anyway). It takes
two arguments, one is what to set and the other is what to set
to. We want to set a mark, so we need to give it an expression
like "'a" to set mark a. To figure out which letter is the
mark supposed to be applied to, we use:
matchstr(getline('.', '(\zs\w')
Here we search for the first letter after the first parenthesis
(with (\zs\w) on the text of our current line, retrieved with
getline(). Search for the help of all these functions. This is
concatenated to a quote, with "'" . {expr} to make a "'x" if
the letter was x.
Then for the second argument, if must be an array similar to what
getpos() returns. Search the help again. However we are
modifying it, to set the mark on the beginning of the next
line. Thus we only use the line item returned by getpos(),
which is the second item, and add 1 to it.
[{buffer}, {line}, {col}, {virtual-offset}]
[0, getpos('.')[1]+1, 1, 0]
the working solution based on previous answer:
au BufRead *.c silent! g/vim: \*Marker(\[a-z])/call setpos(
\ "'".matchstr(getline('.'), '(\zs\w'),
\ [0, getpos('.')[1]+1, 1, 0] )
→ thanks to #sidyll for the help.

Best way to create substitution macros in vim

I'd like to set up some custom auto-complete macros in vim. I'm thinking something like this (| represents the cursor position):
it<TAB>|
immediately becomes:
it("|", function () {
});
Is this possible using straight vim, or would I need a plugin? If so, is there a preferred plugin out there?
Using an abbreviation you could write something like this:
inorea it it("", function () {<cr>});<c-o>k<c-o>f"
The purpose of <c-o>k<c-o>f" at the end is to reposition your cursor inside the double quotes, but it may not work all the time.
Using a mapping, you could try this:
ino <expr> <tab> <sid>expand_last_word()
let s:your_expansions = {
\ 'it': '\<c-w>it(\"\", function () {\<cr>});\<c-o>k\<right>',
\ }
fu! s:expand_last_word() abort
let last_word = matchstr(getline('.'), '\v<\k+%'.col('.').'c')
return has_key(s:your_expansions, last_word)
\ ? eval('"'.s:your_expansions[last_word].'"')
\ : "\<tab>"
endfu
You would have to add your abbreviations and their expansions inside the dictionary s:your_expansions.
Using the :read command, you could define larger snippets of code, and split them across several files:
ino <expr> <tab> <sid>expand_last_word()
fu! s:expand_last_word() abort
let last_word = matchstr(getline('.'), '\v<\k+%'.col('.').'c')
if last_word ==# 'it'
return "\<c-w>\<c-o>:r /path/to/some_file\<cr>\<c-o>f\"\<right>"
endif
return "\<tab>"
endfu
Here /path/to/some_file should contain your snippet:
it("", function () {
});
They are very simple solutions, if you want something more robust, you probably need a snippets plugin. One of them is UltiSnips, which requires that your Vim version has been compiled with Python support (:echo has('python') or :echo has('python3') returns 1).
With UltiSnips, you would write your snippet like this:
snippet it "your description" b
it("$0", function () {
});
endsnippet
Here the definition is included between the keywords snippet and endsnippet. On the 1st line, you can describe the purpose of your snippet, inside the string in double quotes. It will be displayed by UltiSnips inside a list, if you've defined several snippets with the same tab trigger, and there's an ambiguity.
The ending b is an option to prevent the tab trigger it from being expanded anywhere else than the beginning of a line.
$0 is a tabstop, it stands for the position in which you want the cursor to be, once the snippet has been fully expanded.
The readme page on github gives a quick start, and some links to videos.
If you want to have a look at the snippets written by other people, you can install vim-snippets.
There are other snippet managers but I don't know them well enough to describe the syntax of their snippets. If you want a comparison, here's one, and here are links for some of them:
snipmate
mu-template
neosnippet
xptemplate
Here is a abbreviation that you can use for your particular example
:inoreabbrev it it("", function () {<cr>});<esc>kf"a
Typing it followed by ctrl + ] in insert mode will render
it("|", function () {
});
and keep you in insert mode.
But I would definitely go for ultisnips and there is a screencast for creating snippets on the same page. That's why I am omitting the snippet here as you can do it yourself.

How do I use a map in combination with search matches?

How do I use a map on every match found after a search?
I have created various functions which I invoke using a map.
I would like to use the maps on every search matches found.
If I search for dates in my text, how would I apply a i/v/nmap on every search-match found?
something like this?
%s/search-pattern/=\normal mode map/g
%s/search-pattern/=\insert mode map/g
Is it possible also to combine maps?
Hope I made myself clear.
Vim is quite powerful, and I suspect insert mode/normal mode maps are not the most convenient approach here.
Some idioms that may get you started:
Edit: I've built on your earlier question (
How do I visual select a calculation backwards?
) and provided a demo, explained in
chat
1. Record a macro:
qqniMyText<Esc>q
This will insert 'MyText' at each match position. Now, repeat a hundred times: 100#q
(consider setting :se nowrapscan to avoid restarting from the top).
2. Use :global
:g/somepattern/norm! Aappended<Esc>
will append the text 'appended' to each line containing the search pattern
3. Use smart substitutions:
You can do some 'static' edit actions using replacement patterns:
:%s/\v(\d\d)-(\d\d)-(\d{4})/\3\2\1/g
To transform dd-mm-yyyy into yyyymmdd date stamps.
To do a dynamically evaluated substitution (using vimscript with \= in the replacement expression) you can do virtually anything (including, sending mail or printing a document, if you would really want to):
:%s/\v<DB_\w+>/\=substitute(submatch(0), '\v_?([^_])([^_]*)', '\U\1\L\2', 'g')/g
To transform 'database style' names like
var DB_USER_ID = f();
var DB_USER_FIRST_NAME = f();
var DB_USER_LAST_NAME = f();
var DB_USER_HOME_ADDRESS = f();
into 'camel case style names' like:
var DbUserId = f();
var DbUserFirstName = f();
var DbUserLastName = f();
var DbUserHomeAddress = f();
Live demo with expression evaluations
Edit In response to the comment/chat: You can use the approach #1 for this quite easily:
/\v\c\s*\zs(\s{-}(((sqrt|log|sin|cos|tan|exp)?\(.{-}\))|(-?[0-9,.]+(e-?[0-9]+)?)|([-+*/%^]+)))+(\s*\=?)?\s*
qqa<M-.><Esc>nq
Now you can repeat for all of the document:
:set nowrapscan
100#q
If there's only one match in every line, you could use :global instead of :s:
:%g/search-pattern/normal nrX
The :[range]normal positions the cursor at the beginning of the line, therefore the n to go to the first match before the mapping (I use rX as an example). You could write a custom command that would handle all matches in a line, but I would solve your use case with a recursive macro instead:
First, perform the search: /search-pattern, then record a macro containing your mapping, which jumps to the next match at the end: qarXnq. You can now manually apply the macro repeatedly via #a, or make it recursive via qA#aq, or :let #a .= '#a'. Execute this once #a, and it will run until it runs out of matches.

Resources