i'm trying to do a command that fills up the current line with # , then goes to the begining and rewrites with the date and time, then o, this is my attempt:
:nnoremap <F6><Home>
set count = 0
normal i
while count <= columns
normal #
set count += 1
endwhile
<Home>R =strftime("%c")<CR>
normal o
the result of pressing F6 should be somehting like:
Date and time######################################################################
but yeah this is my first attempt at extending vim so please help me understand how to make this allright.
The right-hand side of a mapping is basically the same as if you typed the commands. This allows you to gradually move from repeatedly typing something to recording a macro (#q), to finally making a permanent mapping.
Here's a somewhat literal translation of your attempt:
:nnoremap <F6> 80I#<Esc>0R<C-r>=strftime("%c")<CR><Esc>o
It uses a fixed width of 80. If you want something more dynamic (like the value of 'textwidth' when it's > 0), you have to move from direct execution to programmatic interpolation via :execute, like this:
:nnoremap <F6> :execute 'normal' (&textwidth > 0 ? &textwidth : 80) . "I#\<lt>Esc>"<CR>0R<C-r>=strftime("%c")<CR><Esc>o
As you can see, this is already way more cryptic, with a mixture of Ex commands and :normal mode, quoting, etc.
The final step is a fully configurable and reusable plugin. Only for those, with elaborate error handling and stuff, you'd typically use separate :function definitions, which are then just called from your mapping:
function! s:InsertSeparator()
...
endfunction
nnoremap <silent> <F6> :call <SID>InsertSeparator()<CR>
You can use printf in vimscript.
:nnoremap <F6> I<C-r>=substitute(printf("%-80s", strftime("%c")), '\(\s\)\#<=\s', '#', 'g')<CR><Esc>o
Related
Vim features an internal variable textwidth, which determines how many characters will be printed on screen before adding an <EOL> character to wrap the text to a next line.
I would like to create a mapping, let's say <c-j>, for which I would like the cursor to move a number of characters to the right equal to the value stored in textwidth. This would simulate "going down a line" when the text is wrapped.
I assume that a simple approach would be along the lines of:
nnoremap <c-j> {textwidth}l
However, I have not found a way of evaluating the value of textwidth so that it cant be used as a count for the command l.
Any help is welcome!
There are two ways: One is the interpolation of &textwidth (the & sigil turns the option name into a variable that contains its value; cp. :help :let-option), as in #RuslanOsmanov's answer:
nnoremap <silent> <C-j> :execute "normal!" &textwidth . 'l'<CR>
Another is :help :map-expression, which automatically evaluates the mapping's right-hand side as a Vimscript expression. I would prefer this one, as it's shorter:
nnoremap <expr> <C-j> &textwidth . 'l'
Further improvements
You probably should consider what to do if 'textwidth' is unset, i.e. zero. Unhandled, this would result in a 0l motion, i.e. going to the second character in the line. You can use a conditional to turn this into a no-op, for example. (Or make it beep by returning '<Esc>' instead of '').
nnoremap <expr> <C-j> (&textwidth == 0 ? '<Esc>' : &textwidth . 'l')
Really needed?
Vim has a :help gj command (and variants for the other directions) built-in, that does something similar to what you're trying to implement. Unless you're attempting to solve a special case (e.g. disregarding options like 'showbreak' that further reduce the amount of characters actually shown), it would be advisable to just use (and maybe remap) the built-ins.
You can refer to an option value by prefixing its name with an ampersand, e.g. &textwidth.
Moving &textwidth characters to the right can be run as follows:
:execute "normal!" &textwidth 'l'
where the arguments ("normal!", &textwidth, and 'l') are concatenated with a space and executed as an Ex command.
So your mapping might look something like this:
:nnoremap <silent> <c-j> :execute "normal!" &textwidth 'l'<cr>
I've got the following vimscript function:
function! WindowCommand(cmd)
execute a:cmd
if !g:golden_ratio_enabled
normal <C-w>=
endif
endfunction
And I use it like so:
map <space>w/ :call WindowCommand(':vs')<cr>
It's supposed to equalize the windows, but only if g:golden_ratio_enabled is 0, otherwise it should do nothing.
It doesn't work, though, and I'm not sure why, because the following DOES work:
map <space>w/ :vs<cr><C-w>=
What am I doing wrong here?
There are a couple fixes. Thankfully, the fix is really simple
For whatever reason, normal <C-w>foo does not work; You must use wincmd instead. From :h wincmd
*:winc* *:wincmd*
These commands can also be executed with ":wincmd":
:[count]winc[md] {arg}
Like executing CTRL-W [count] {arg}. Example: >
:wincmd j
Moves to the window below the current one.
This command is useful when a Normal mode cannot be used (for
the |CursorHold| autocommand event). Or when a Normal mode
command is inconvenient.
The count can also be a window number. Example: >
:exe nr . "wincmd w"
This goes to window "nr".
So in this case, you should do
wincmd =
Alternatively, you could enter a literal <C-w> character, by typing <C-v><C-w>. In your vim session, this character will be displayed as ^W.
To execute actions with <notation>, use instead:
:exe "normal \<notation>"
I use it a lot to debug mappings.
But in this case, prefer indeed wincmd.
I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:
imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif
What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.
What I had originally was this:
imap <c-c> <Esc>^i//
And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.
I completely agree with #Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.
However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).
function! ToggleComment()
let pos=getpos(".")
let win=winsaveview()
if getline(".") =~ '\s*\/\/'
normal! ^2x
let pos[2]-=1
else
normal! ^i//
let pos[2]+=3
endif
call winrestview(win)
call setpos(".",pos)
startinsert
endfunction
inoremap <c-c> <Esc>:call ToggleComment()<CR>
Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.
This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.
There are many commenting plugins for vim:
commentary.vim
tComment
EnhCommentify
NERD Commenter
and many more at www.vim.org
I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.
As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.
If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel
for writing plugins the proper way.
Example of a simple expression mapping for toggling comments:
nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text
i use the bottom one with the
...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>
,c comments out a region
,u uncomments a region
Is there a way to unfold code when going to a line number? For instance I type :35 where line 35 is folded, then I have to unfold that section manually to actually get to that line. I would like to type :35and have that code then unfolded automatically and my cursor put on line 35 without any further key presses.
If you use the 35G command instead of :35, you can achieve this with the following mapping:
"[count]G Also open fold under cursor when supplying [count] (i.e.
" jumping to a particular line, not the end of the
" buffer). Use [count]|gg| if you don't want this.
nnoremap <expr> G (v:count ? 'Gzv' : 'G')
For :35 itself, this would be hard to achieve. You would have to intercept the <CR> via a :cmap <expr>, check the typed command via getcmdtype() and getcmdline(), and, if it's a number, manipulate the command, i.e. append normal! zv to it; like this:
cmap <expr> <CR> getcmdtype() == ':' && getcmdline() =~ '^\d\+$' ? 'normal! zv<CR>' : '<CR>'
zv. From :help zv:
View cursor line: Open just enough folds to make the line in
which the cursor is located not folded.
While this command could probably be triggered automatically in some way, I have not come across it yet. Using the command as-is has served me well, though.
Define a new command mapping. In this example, I chose \gz:
:nmap \gz gg<Bar>zO
I use "very magic" for regexp searches (i.e. /\v or %s/\v) but I wish I could set some option so I don't have to include \v anymore, anywhere. Is there a way to do this?
Not directly, however you can always use a mapping:
:nnoremap / /\v
:cnoremap %s/ %s/\v
Even if you could set 'very magic' in the way you can set nomagic, you really wouldn't want to as it would break pretty much every plugin in existence.
Edit
See also this page.
EDIT2: I just discovered this plugin, which may be better than the remapping solutions (which seem to have some unavoidable drawbacks; see below). I haven't tested it yet, though, so I don't know if it behaves exactly as desired.
http://www.vim.org/scripts/script.php?script_id=4849
EDIT3: I've been using the plugin for about a year and a half, and I love it. It still interferes with search history, however (see below), and it also breaks incsearch, so I have the following in my Vim config:
" Since I use incsearch:
let g:VeryMagic = 0
nnoremap / /\v
nnoremap ? ?\v
vnoremap / /\v
vnoremap ? ?\v
" If I type // or ??, I don't EVER want \v, since I'm repeating the previous
" search.
noremap // //
noremap ?? ??
" no-magic searching
noremap /v/ /\V
noremap ?V? ?\V
" Turn on all other features.
let g:VeryMagicSubstituteNormalise = 1
let g:VeryMagicSubstitute = 1
let g:VeryMagicGlobal = 1
let g:VeryMagicVimGrep = 1
let g:VeryMagicSearchArg = 1
let g:VeryMagicFunction = 1
let g:VeryMagicHelpgrep = 1
let g:VeryMagicRange = 1
let g:VeryMagicEscapeBackslashesInSearchArg = 1
let g:SortEditArgs = 1
I used DrAI's suggestion for a while, but found it frustrating in practice because of the following behavior:
If you type the following:
/{pattern}
:%s//{replacement}
...then, without this mapping, you can see what you're about to replace before you do a replacement. But with the remapping, you suddenly have s/\v/ instead of s//; this matches eveything in the file, which is obviously wrong.
Fortunately, the s command itself has an alternative form that uses very magic for its search. So here are the mappings I'm currently using in my .vimrc:
nnoremap / /\v
vnoremap / /\v
cnoremap %s/ %smagic/
cnoremap >s/ >smagic/
nnoremap :g/ :g/\v
nnoremap :g// :g//
Note that just mapping s/ leads to problems when attempting to use a pattern that ends in s; similarly, mapping g/ would create problems when using patterns ending in g. Note that the :g/ and :g// mappings prevent Vim from showing the command immediately.
EDIT: Unfortunately, there does not appear to be a "magic" version of :global, which is why the seemingly-superfluous mapping of :g// is used to ensure that the global command can use the previous search pattern.
Another drawback is that these remappings interfere with search history. As an example, consider using * to search for the next occurrence of the word under the cursor. This causes Vim to search for the pattern \<[word]\>, which does not start with \v. Without the remappings described above, typing / and pressing the up arrow will recall that search pattern. With the remappings, however, after typing /, you must delete the automatically-inserted \v before pressing the up arrow in order to recall that pattern.
To reply to the answer above as I can't comment yet, from How to make substitute() use another magic mode?, the vim docs and my own testing, smagic (and sm) only enters magic mode and not very magic mode.
*:snomagic* *:sno*
:[range]sno[magic] ... Same as `:substitute`, but always use 'nomagic'.
{not in Vi}
*:smagic* *:sm*
:[range]sm[agic] ... Same as `:substitute`, but always use 'magic'.
{not in Vi}
For example, one should ('s turn into )'s in a file with :%sm/(/)/g and not :%sm/\(/\)/g, which shows the following for me
E54: Unmatched \(
E54: Unmatched \(
E476: Invalid command
Instead, to enter very magic mode, one should use \v in the search expression of substitute (i.e. :%s/\v\(/\)/g)
(Please correct me if I've messed up, I am quite new to Vim)
https://learnvimscriptthehardway.stevelosh.com/chapters/31.html#magic
Add a normal mode mapping that will automatically insert the \v for you whenever you begin a search
my current config in init.vim:
(I want to go to the fist line and then search, and return back using 's, so msgg)
" vim has set: au BufNewFile,BufRead *.ahk setf autohotkey
if &filetype == 'vim'
nnoremap / msgg/\v^[^"]*
elseif &filetype == 'autohotkey'
echo 'ahk'
nnoremap / msgg/\v^[^;]*
" todo
" https://github.com/hnamikaw/vim-autohotkey
elseif expand('%:t') == 'wf_key.ahk'
nnoremap / msgg/\v^[^;]*
elseif &filetype == 'zsh'
nnoremap / msgg/\v^[^#]*
else
" vscode neovim can not detect filetype?
nnoremap / msgg/\v^[^#";(//)(/*)]*
endif
nnoremap ? msgg/\v
" nnoremap / /\v
cnoremap s/ s/\v
todo: plugin for very magic
https://github.com/coot/EnchantedVim