Using template files in Vim - vim

I am trying to use a template for Ruby files, by adding this to my .vimrc:
function! RubyTemplate()
" Add pragma comment
call setline(1, '# frozen_string_literal: true')
" Add two empty lines
call append(1, repeat([''], 2))
" Place cursor on line number 3
call cursor(3, 0)
endfunction
autocmd BufNewFile *.rb :call RubyTemplate()
However, this doesn't work and when I open a new Ruby file, it's empty.
Everything works as expected if I issue an :e! afterwards. However, this doesn't work if I add e! to the function, so I have to manually fire it every time.
What am I doing wrong?

You can use a static template file instead of invoking a function.
For instance, you can create a template file for your ruby scripts in your vim directory as ~/.vim/skeletons/ruby.skel, with the desired contents.
1 # frozen_string_literal: true
2
3
Then in your vimrc you should add the following code:
" Skeleton for .rb files
augroup ruby
" Remove all existing autocommands in the group
au!
au BufNewFile *.rb 0r ~/.vim/skeletons/ruby.skel
augroup end

Noah Frederick has an elegant solution that allows us to insert snippets manually or automatically.
It uses ultisnips plugin and two files
" after/plugin/ultisnips_custom.vim
if !exists('g:did_UltiSnips_plugin')
finish
endif
augroup ultisnips_custom
autocmd!
autocmd BufNewFile * silent! call snippet#InsertSkeleton()
augroup END
and
" autoload/snippet.vim
function! s:try_insert(skel)
execute "normal! i_" . a:skel . "\<C-r>=UltiSnips#ExpandSnippet()\<CR>"
if g:ulti_expand_res == 0
silent! undo
endif
return g:ulti_expand_res
endfunction
function! snippet#InsertSkeleton() abort
let filename = expand('%')
" Abort on non-empty buffer or extant file
if !(line('$') == 1 && getline('$') == '') || filereadable(filename)
return
endif
call s:try_insert('skel')
endfunction
In my case, I have done some changes but now if I create an empty python file, for example, I end up with:
An important note: In my case, if vim or neovim is not detecting the filetype correctly, and it can be done with auto commands, your automatic snippet insertion will not work.

Related

How to automatically make a Shebang line for the files made in vi?

I tried some Vim functions like VimLeave and VimLeavePre to execute a script which adds the Shebang line automatically to all the files as soon as Vi is quitted.
But the problem occurs when we don't save the file and just quit.
The vi functionality should not be altered which means when we give vi filename it opens that file and when we close without writing, the file will not be created. In this case, the script should not be executed.
One more feature is, this script should be executed only for the first time of write.
Is it possible to write such a code?
I would recommend reading this Noah Frederick's article File Templates with UltiSnips and Projectionist. besides the article, he also has a video
The great advantage of this method is that you get a dynamic Shebang instead of a static one, because most of us already have some sort of snippets plugin installed, and using static file skeletons wouldn't be in consonance with this solutions.
Note: The article is a little bit long but I guarantee you it worth reading.
For my personal use, I have adapted Noah's solution with something like this:
" File: ultisnips_custom.vim - Custom UltiSnips settings
" Maintainer: Sergio Araújo
" Oririnal Creator: Noah Frederick
" Reference: https://noahfrederick.com/log/vim-templates-with-ultisnips-and-projectionist
" Last Change: nov 20 2019 08:53
" Place it at: after/plugin/ultisnips_custom.vim
" We need python or python3 to run ultisnips
if !has("python") && !has("python3")
finish
endif
" This function is called by the autocommand at the end of the file
function! TestAndLoadSkel() abort
let filename = expand('%')
" Abort on non-empty buffer or extant file
if !(line('$') == 1 && getline('$') == '') || filereadable('%')
return
endif
" Load UltiSnips in case it was deferred via vim-plug
if !exists('g:did_plugin_ultisnips') && exists(':PlugStatus')
call plug#load('ultisnips')
doautocmd FileType
endif
" the function feedkys simulates the insert key sequence in order to call
" the template (skel)
execute 'call feedkeys("i_skel\<C-r>=UltiSnips#ExpandSnippet()\<CR>")'
endfunction
augroup ultisnips_custom
autocmd!
au Bufnewfile *.sh,*.zsh,*.html,*.css,*.py,*.tex,*.md,*.vim :call TestAndLoadSkel()
"autocmd BufEnter *.sh,*.zsh,*.html,*.py execute 'call feedkeys("i_skel\<c-j>")'
augroup END
" vim: fdm=marker:sw=2:sts=2:et
Pay attention to the place where you have to put this file:
after/plugin/ultisnips_custom.vim
Note: For each filetype you want a Shebang you must create a snippet called _skel or you can adapt the code to fit your needs.
For my vim snippets I have this one:
snippet _skel "file header" w
" file: ${1:`!p snip.rv = snip.fn`}
" author: `!v g:snips_author`
" site: `!v g:snips_site`
" Created: `!v strftime("%b %d, %Y - %H:%M")`
"Last Change: `!v strftime("%b %d, %Y")`
${0}
endsnippet
On my vimrc I have some predefined variables that I can use on my snippets or _skel files:
let g:snips_author='Sergio Araujo'
let g:snips_site='https://dev.to/voyeg3r'
let g:snips_email='<voyeg3r ✉ gmail.com>'
let g:snips_github='https://github.com/voyeg3r'
let g:snips_twitter='#voyeg3r'
If you need to manipulate buffer contents you should trap BufWrite instead. The script could then be executed many times, of course, but that's not a big deal, as we can easily guard against multiple buffer changes. For example,
augroup AddShebang | au!
autocmd BufWrite * call s:add_shebang()
augroup end
let s:known_shebang = {
\ 'awk': '/usr/bin/awk -f',
\ 'perl': '/usr/bin/perl -w',
\ 'python': '/usr/bin/env python',
\ }
function s:add_shebang()
let l:cmd = get(s:known_shebang, &filetype)
" filetype is known but shebang was not added yet
if !empty(l:cmd) && getline(1) !~# '^#!'
call append(0, '#!'..l:cmd)
endif
endfunction

How to auto highlight the current word in vim?

For example, our text is:
hello world
abcd hello world
hello world
In eclipse, when your cursor is at some word, the word hello is auto highlight in the current file. When you type ww in normal mode, the cursor is at other word world will highlight in the current file, the hello is un-highlighted automatically. This feature is very convenient for users.
Does vim can do this with some plugin or else?
Something like this?
set updatetime=10
function! HighlightWordUnderCursor()
if getline(".")[col(".")-1] !~# '[[:punct:][:blank:]]'
exec 'match' 'Search' '/\V\<'.expand('<cword>').'\>/'
else
match none
endif
endfunction
autocmd! CursorHold,CursorHoldI * call HighlightWordUnderCursor()
This won't clobber the search register but will use the same highlighting as would normally be used. (If you want a different highlight color change Search to that highlight group.) A short update time is needed so that the CursorHold event it fired fairly often. It also won't highlight anything if the cursor is above punctuation or whitespace.
The iskeyword setting determines what is considered part of a word when expand('<cword>') is used.
Improving #FDinoff's amazing answer, with custom highlight - dark BG and underline, and disable on quickfix list, fugitive filetype and when on diff:
function! HighlightWordUnderCursor()
let disabled_ft = ["qf", "fugitive", "nerdtree", "gundo", "diff", "fzf", "floaterm"]
if &diff || &buftype == "terminal" || index(disabled_ft, &filetype) >= 0
return
endif
if getline(".")[col(".")-1] !~# '[[:punct:][:blank:]]'
hi MatchWord cterm=undercurl gui=undercurl guibg=#3b404a
exec 'match' 'MatchWord' '/\V\<'.expand('<cword>').'\>/'
else
match none
endif
endfunction
augroup MatchWord
autocmd!
autocmd! CursorHold,CursorHoldI * call HighlightWordUnderCursor()
augroup END
Yes there is a vim plugin for highlighting the occurances of a word automatically. This one is implemented exclusively for $variables and ->properties in .php files.
DEMO :
And here is the same one but adapted for Perl files.
DEMO :
May be you can modify it for your purpose.
There's a script on vim.wikia.com for doing exactly that. It waits until you've stopped moving the cursor and then highlights all instances of the current word. You can then use n and N to jump between them like you normally would with search results.
I'm copying it here in case the link goes down:
" Highlight all instances of word under cursor, when idle.
" Useful when studying strange source code.
" Type z/ to toggle highlighting on/off.
nnoremap z/ :if AutoHighlightToggle()<Bar>set hls<Bar>endif<CR>
function! AutoHighlightToggle()
let #/ = ''
if exists('#auto_highlight')
au! auto_highlight
augroup! auto_highlight
setl updatetime=4000
echo 'Highlight current word: off'
return 0
else
augroup auto_highlight
au!
au CursorHold * let #/ = '\V\<'.escape(expand('<cword>'), '\').'\>'
augroup end
setl updatetime=500
echo 'Highlight current word: ON'
return 1
endif
endfunction
As noted in a comment on that page, if you always want this feature on you can just call the function from your vimrc after defining it. That way you can use z/ (or whatever shortcut you assign it to) to turn it off again later.

What autocmd FileType is in .vimrc?

I started using twitvim and found this code code for .vimrc.
But I am not sure what the last part of code is trying to do.
autocmd FileType twitvim call s:twitvim_my_settings()
function! s:twitvim_my_settings()
set nowrap
endfunction
And this is the original code. I deleted all of <C-u>, <C-w> and j.
""" twitvim
let twitvim_count = 40
nnoremap ,tp :<C-u>PosttoTwitter<CR>
nnoremap ,tf :<C-u>FriendsTwitter<CR><C-w>j
nnoremap ,tu :<C-u>UserTwitter<CR><C-w>j
nnoremap ,tr :<C-u>RepliesTwitter<CR><C-w>j
nnoremap ,tn :<C-u>NextTwitter<CR>
autocmd FileType twitvim call s:twitvim_my_settings()
function! s:twitvim_my_settings()
set nowrap
endfunction
While Kent's answer is correct and very detailed, I thought I'd submit a visual version.
autocmd FileType twitvim call s:twitvim_my_settings()
|______________________| |__| | |___________________|
| | | |
1 2 3 4
1. Automatically do something when the FileType (the file extension) matches .twitvim
2. call (Vim's way to run a function)
3. Refers to a function local to the current script file
4. The function to be ran
I don't use the plugin, but I could try to explain what the meaning of those lines:
"this just assigning a variable, may be used by the plugin
let twitvim_count = 40
" create a normal mode mapping, to execute Posttotwitter command.
" The leading ctrl-u just for removing the range information of the
"command line. if you removed it, range info will be kept
" all following commands have the same meaning for <c-u>
nnoremap ,tp :<C-u>PosttoTwitter<CR>
" the next 4 commands are same as above, but executing different cmd. the ctrl-w j will move cursor
" to a "belower" window, the cmd may open a new window/split.
nnoremap ,tf :<C-u>FriendsTwitter<CR><C-w>j
nnoremap ,tu :<C-u>UserTwitter<CR><C-w>j
nnoremap ,tr :<C-u>RepliesTwitter<CR><C-w>j
nnoremap ,tn :<C-u>NextTwitter<CR>
"create autocmd, if the filetype is twitvim, call a function
autocmd FileType twitvim call s:twitvim_my_settings()
"here a function was defined
function! s:twitvim_my_settings()
"this function just do one thing, set nowrap option. (text is gonna be displayed without wrap.)
set nowrap
endfunction

Can an autocmd be turned on/off?

I have follwing in my .vimrc to hightlight all words that matches the one on current cursor
autocmd CursorMoved * silent! exe printf('match Search /\<%s\>/', expand('<cword>'))
But sometimes it is a little annoying, so I'd like to map a key to turn on or off it, e.g. <F10>
How can I do this?
Clear the autocommand and remove highlight:
nmap <f8> :autocmd! CursorMoved<cr> :call clearmatches()<cr>
and to turn it back on using a different key:
nmap <f9> :autocmd CursorMoved * silent! exe printf('match Search /\<%s\>/', expand('<cword>'))<cr>
Put the following in your .vimrc:
let g:toggleHighlight = 0
function! ToggleHighlight(...)
if a:0 == 1 "toggle behaviour
let g:toggleHighlight = 1 - g:toggleHighlight
endif
if g:toggleHighlight == 0 "normal action, do the hi
silent! exe printf('match Search /\<%s\>/', expand('<cword>'))
else
"do whatever you need to clear the matches
"or nothing at all, since you are not printing the matches
endif
endfunction
autocmd CursorMoved * call ToggleHighlight()
map <F8> :call ToggleHighlight(1)<CR>
The idea is, if you call the function with an argument it changes the behavior to print/no print.
The autocommand just uses the last setting because the function there is called without an argument.

Can vim monitor realtime changes to a file

My question is similar to this how to monitor a text file in realtime
but I want to do it in vim. I know I can read an opened file use tail -f sample.xml file, and when new content is written to the file, it'll also write the new content to my screen. Can I have vim automatically fill the new data when a file is updated?
You can :set autoread so that vim reads the file when it changes. However (depending on your platform), you have to give it focus.
From the help:
When a file has been detected to have
been changed outside of Vim and it
has not been changed inside of Vim,
automatically read it again. When the
file has been deleted this is not
done.
Don't know about automatically, but you can type:
:e!
to reload the file
Put the following in your .vimrc:
" check one time after 4s of inactivity in normal mode
set autoread
au CursorHold * checktime
like #flukus said in a comment to a previous answer you can call feedkeys["lh"] (it moves the cursor to the right and back to the left, which normaly doesn't do harm when viewing a log file)
So, if you combine the rest of the answer you have a oneliner you can run from ex (whithin vim) when needed:
:set autoread | au CursorHold * checktime | call feedkeys("lh")
***(if you would want to jump (nearly) to the end of the file, just use "G" instead of "lh" with feedkeys)***
Explanation:
autoread: reads the file when changed from the outside (but it doesnt work on its own, there is no internal timer or something like that. It will only read the file when vim does an action, like a command in ex :!
CursorHold * checktime: when the cursor isn't moved by the user for the time specified in 'updatetime' (which is 4000 miliseconds by default) checktime is executed, which checks for changes from outside the file
call feedkeys("lh"): the cursor is moved once, right and back left. and then nothing happens (... which means, that CursorHold is triggered, which means we have a loop)
Additionally you can :set syntax=logtalk to color the log
To stop the scrolling when using call feedkeys("G"), execute :set noautoread - now vim will tell, that the file was change ans ask if one wants to read the changes or not)
(Does this have any sideeffects?)
Edit: I see one side-effect: if one uses "G" as the feedkey, it will scroll down every currently opened buffer?! So, it's not possible to work in the left buffer of a splittet window while having the right buffer scroll down a logfile automatically
Edit2: Another side effect is that when you enter the command line window(by using q:) an error message always pops up.
Stick this in your .vimrc and it should work like a boss. (Taken from: http://vim.wikia.com/wiki/Have_Vim_check_automatically_if_the_file_has_changed_externally)
" Function to Watch for changes if buffer changed on disk
function! WatchForChanges(bufname, ...)
" Figure out which options are in effect
if a:bufname == '*'
let id = 'WatchForChanges'.'AnyBuffer'
" If you try to do checktime *, you'll get E93: More than one match for * is given
let bufspec = ''
else
if bufnr(a:bufname) == -1
echoerr "Buffer " . a:bufname . " doesn't exist"
return
end
let id = 'WatchForChanges'.bufnr(a:bufname)
let bufspec = a:bufname
end
if len(a:000) == 0
let options = {}
else
if type(a:1) == type({})
let options = a:1
else
echoerr "Argument must be a Dict"
end
end
let autoread = has_key(options, 'autoread') ? options['autoread'] : 0
let toggle = has_key(options, 'toggle') ? options['toggle'] : 0
let disable = has_key(options, 'disable') ? options['disable'] : 0
let more_events = has_key(options, 'more_events') ? options['more_events'] : 1
let while_in_this_buffer_only = has_key(options, 'while_in_this_buffer_only') ? options['while_in_this_buffer_only'] : 0
if while_in_this_buffer_only
let event_bufspec = a:bufname
else
let event_bufspec = '*'
end
let reg_saved = #"
"let autoread_saved = &autoread
let msg = "\n"
" Check to see if the autocommand already exists
redir #"
silent! exec 'au '.id
redir END
let l:defined = (#" !~ 'E216: No such group or event:')
" If not yet defined...
if !l:defined
if l:autoread
let msg = msg . 'Autoread enabled - '
if a:bufname == '*'
set autoread
else
setlocal autoread
end
end
silent! exec 'augroup '.id
if a:bufname != '*'
"exec "au BufDelete ".a:bufname . " :silent! au! ".id . " | silent! augroup! ".id
"exec "au BufDelete ".a:bufname . " :echomsg 'Removing autocommands for ".id."' | au! ".id . " | augroup! ".id
exec "au BufDelete ".a:bufname . " execute 'au! ".id."' | execute 'augroup! ".id."'"
end
exec "au BufEnter ".event_bufspec . " :checktime ".bufspec
exec "au CursorHold ".event_bufspec . " :checktime ".bufspec
exec "au CursorHoldI ".event_bufspec . " :checktime ".bufspec
" The following events might slow things down so we provide a way to disable them...
" vim docs warn:
" Careful: Don't do anything that the user does
" not expect or that is slow.
if more_events
exec "au CursorMoved ".event_bufspec . " :checktime ".bufspec
exec "au CursorMovedI ".event_bufspec . " :checktime ".bufspec
end
augroup END
let msg = msg . 'Now watching ' . bufspec . ' for external updates...'
end
" If they want to disable it, or it is defined and they want to toggle it,
if l:disable || (l:toggle && l:defined)
if l:autoread
let msg = msg . 'Autoread disabled - '
if a:bufname == '*'
set noautoread
else
setlocal noautoread
end
end
" Using an autogroup allows us to remove it easily with the following
" command. If we do not use an autogroup, we cannot remove this
" single :checktime command
" augroup! checkforupdates
silent! exec 'au! '.id
silent! exec 'augroup! '.id
let msg = msg . 'No longer watching ' . bufspec . ' for external updates.'
elseif l:defined
let msg = msg . 'Already watching ' . bufspec . ' for external updates'
end
echo msg
let #"=reg_saved
endfunction
let autoreadargs={'autoread':1}
execute WatchForChanges("*",autoreadargs)
Tail Bundle should do what you want. Note, haven't used it myself.
There is a plugin also:
https://github.com/djoshea/vim-autoread
This was the only way I could make this work on OSX.
If unix + neovim
:term tail -f <filename>
Obviously this won't work for everyone, but it's how I do it.
To enable reloading if you open a specific file, you can add this to your .vimrc and use a modeline like vim: set ft+=.watch:. It takes advantage of the feature to set multiple filetypes for a buffer (see below):
vim9script
command WatchFiles {
autocmd! AUWatchFile FocusGained,VimResume,BufEnter,WinEnter,CursorHold * checktime
autocmd! AUWatchFile BufEnter,InsertEnter,CursorHold,CursorHoldI <buffer> checktime
setlocal autoread
checktime
}
command UnwatchFiles {
autocmd! AUWatchFile
set autoread<
}
# To enable this, you may use e.g. a modeline: `vim: set ft+=.watch`
def WatchAutomatically()
# Check if the "list" of filetypes (a dot separated string) contains 'watch'.
if -1 != match(&filetype, "\\(^\\|\\.\\)watch\\($\\|\\.\\)")
WatchFiles
endif
enddef
augroup AUWatchFile | augroup END
autocmd BufWinEnter * call WatchAutomatically()
More details
You are able to set multiple filetypes separated by .:
When a dot appears in the value then this separates two filetype
names. Example:
/* vim: set filetype=c.doxygen : */ ~
See :help 'filetype'.
Keep in mind that you should manipulate the filetype first and set additional options after that in the modeline. Otherwise these options may be overridden by the settings specific for the filetype.
Btw, the above is a Vim9 script (which I discovered today). Translating it back to a good-old Vim script is trivial:
Use " for comments.
Transform multi-line commands like this:
command UnwatchFiles
\ autocmd! AUWatchFile
\ | set autoread<
Remove the vim9script line.
See :help Vim9-script for details.
Shortcomings
Reloading is not limited to the buffer which contains the modeline. You may disable it again with :UnwatchFiles.
It is still dump polling.
VIM will warn you when a file has been updated so that you don't overwrite changes that have been made since you opened it. It will prompt you at that point to reload the file.

Resources