Making silent function non silent in vim - vim

I use the the template-file-loader vim script to load a template file when I create p.x. a new latex file.
The template-file-loader script is able to execute a custom TemplateFileFunction_tex when I edit a new tex file..
fun! TemplateFileFunc_tex()
let tex_templates = "$HOME/.vim/templates/tex/"
let choice = confirm("Which template should i load",
\ "&presentation\n" .
\ "&hd-presentation\n" .
\ "&paper\n" .
\ "hd-pape&r\n" .
\ "&xelatex-default\n")
if choice == 1 " presentation
execute "0r " . expand(tex_templates . "presentation.tex")
" [...]
endfun
The problem is the template-file-loader plugins calls the function with silent.
How do I without "unsilent" my custom function without changing the plugin?

I don't see a way to make confirm receive user input in silenced mode. But you can use getchar instead:
let variants=['&presentation', '&hd-presentation', ...]
echohl MoreMsg
unsilent echo "Which template should I load\n".join(variants, "\n")
echohl None
let reply=getchar()
if type(reply)==type(0)
let reply=nr2char(reply)
endif
if reply is# "\n"
let choice=1
else
let replkeys=map(copy(variants), 'tolower(v:val[stridx(v:val, "&")+1])')
let choice=index(replkeys, reply)+1
endif
if choice==1
...

This is another template-expander, but still, mu-template handles correctly questions to the end-user. e.g.
VimL: " #file {rtp}/templates/tex.template
VimL: " way 1:
VimL: " instructions continued on several lines are not supported
VimL: let s:tex_kind = CONFIRM("Which template should i load", "&presentation\n&hd-presentation\n&paper\nhd-pape&r\n&xelatex-default\n", 1)
VimL: " include {rtp}/templates/tex/internals/presentation.template ?
VimL: if s:tex_kind == 1 | call s:Include('presentation', 'tex/internals') | endif
VimL: " include {rtp}/templates/tex/internals/hd-presentation.template ?
VimL: if s:tex_kind == 2 | call s:Include('hd-presentation', 'tex/internals') | endif
...
VimL: " Way 2
VimL: let s:tex_kind = WHICH("CONFIRM", "Which template should i load", "&presentation\n&hd-presentation\n&paper\nhd-pape&r\n&xelatex-default\n", 1)
VimL: let s:tex_kind = substitute(s:tex_kind, '&', '', 'g')
VimL: call s:Include(s:tex_kind, 'tex/internals')

Related

How to remove digraph highlighting in vim

Using vim or nvim, when I open a new vim terminal digraphs display correctly. See below:
However, whenever I change colorscheme (any, it is not specific to a particular colorscheme) - then the digraphs appear highlighted. The highlighting remains even when switching back to the original colorscheme. This happens with any digraph, not just the one shown in this question.
See below:
Cannot find a way to remove that highlighting, or prevent it happening in the first place. Have tried commands like :highlight nonascii none but had no luck. Any help / suggestions much appreciated.
Most/Some of the colorschemes aren't really made for hotswap.
It seems to be a rather common problem with solarized f.e.
There is a plugin which handles a change cleanly: https://github.com/xolox/vim-colorscheme-switcher (I haven't tested it).
And I copied a bunch of functions for that somewhere which works arround the problems most of the time. I don't know where I got it from, but I want to be clear, it is not my work!
function! s:Find_links() " {{{1
" Find and remember links between highlighting groups.
redir => listing
try
silent highlight
finally
redir END
endtry
for line in split(listing, "\n")
let tokens = split(line)
" We're looking for lines like "String xxx links to Constant" in the
" output of the :highlight command.
if len(tokens) == 5 && tokens[1] == 'xxx' && tokens[2] == 'links' && tokens[3] == 'to'
let fromgroup = tokens[0]
let togroup = tokens[4]
let s:known_links[fromgroup] = togroup
endif
endfor
endfunction
function! s:Restore_links() " {{{1
" Restore broken links between highlighting groups.
redir => listing
try
silent highlight
finally
redir END
endtry
let num_restored = 0
for line in split(listing, "\n")
let tokens = split(line)
" We're looking for lines like "String xxx cleared" in the
" output of the :highlight command.
if len(tokens) == 3 && tokens[1] == 'xxx' && tokens[2] == 'cleared'
let fromgroup = tokens[0]
let togroup = get(s:known_links, fromgroup, '')
if !empty(togroup)
execute 'hi link' fromgroup togroup
let num_restored += 1
endif
endif
endfor
endfunction
function! s:AccurateColorscheme(colo_name)
call <SID>Find_links()
exec "colorscheme " a:colo_name
call <SID>Restore_links()
endfunction
command! -nargs=1 -complete=color MyColorscheme call <SID>AccurateColorscheme(<q-args>)

pass vim argument in completefunc

I am writing a vimscript that uses completefunc as:
" GetComp: Menu and Sunroutine Completion {{{1
function! GetComp(arg, findstart, base)
if a:findstart
" locate the start of the word
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
else
echomsg '**** completing' a:base
python << EOF
import vim
import os
flsts = [' ']
path = "."
for dirs, subdirs, files in os.walk(path):
for tfile in files:
if tfile.endswith(('f90', 'F90', 'f', 'F')):
ofile = open(dirs+'/'+tfile)
for line in ofile:
if line.lower().strip().startswith(vim.eval("a:arg")):
modname = line.split()[1]
flsts.append(modname)
vim.command("let flstsI = %s"%flsts)
EOF
if eval("a:arg") = "module"
for m in ["ieee_arithmatic", "ieee_exceptions", "ieee_features", "iso_c_bindings", "iso_fortran_env", "omp_lib", "omp_lib_kinds"]
if m =~ "^" . a:base
call add(flstsI, m)
endif
endfor
elseif eval("a:arg") = "subroutine"
for m in ["alarm()", "date_and_time()", "backtrace", "c_f_procpointer()", "chdir()", "chmod()", "co_broadcast()", "get_command()", "get_command_argument()", "get_environment_variable()", "mvbits()", "random_number()", "random_seed()"]
if m =~ "^" . a:base
call add(flstsI, m)
endif
endfor
endif
return flstsI
endif
endfunction
I will call it for 2 different argument as:
inoremap <leader>call call <C-o>:set completefunc=GetComp("subroutine", findstart, base)<CR><C-x><C-u>
inoremap <leader>use use <C-o>:set completefunc=GetComp("module", findstart, base)<CR><C-x><C-u>
But trying so, gives error: Unknown function GetComp(
I don't know how to call them.
If I don't use arg, then, using this reply, I can call this perfectly.
Kindly help.
You'll have to attach a context to your completion. If you look closely at my message on vi.se, you'll see a framework that permits to bind data to a user-completion. From there in your mapping, it just becomes a question of which context to attach.
A simplified way would be to execute a (preparation) function from the mappings and have the function set script global variables that will be used in your completion function.

Vim not setting `tex` filetype on startup

I noticed that if I open a non-existent HTML file with Vim, the filetype is set automatically to html:
$ vim foobar.html
:set filetype # --> filetype=html
On the other hand, the same does not happen with TeX. If I create a non-existent file the filetype is plaintext, and I have to save the file and reopen it to have it correctly set:
$ vim blabla.tex
:set filetype # --> filetype=plaintext
I also tried with Python, C, VimL, Javascript files and the filetype is always set right away. I don't know why this does not happen with TeX files.
The only thing I can think might interfere is vim-latex. Could that be possible? If so, how do I fix this problem?
If it might be of help, here is my not so long .vimrc file.
Vim uses a giant heuristic to determine plaintex vs other variations of tex.
If you look in $VIMRUNTIME/filetype.vim you will find the following function
" Choose context, plaintex, or tex (LaTeX) based on these rules:
" 1. Check the first line of the file for "%&<format>".
" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
" 3. Default to "latex" or to g:tex_flavor, can be set in user's vimrc.
func! s:FTtex()
let firstline = getline(1)
if firstline =~ '^%&\s*\a\+'
let format = tolower(matchstr(firstline, '\a\+'))
let format = substitute(format, 'pdf', '', '')
if format == 'tex'
let format = 'plain'
endif
else
" Default value, may be changed later:
let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain'
" Save position, go to the top of the file, find first non-comment line.
let save_cursor = getpos('.')
call cursor(1,1)
let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
let cpat = 'start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>'
let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
\ 'cnp', firstNC + 1000)
if kwline == 1 " lpat matched
let format = 'latex'
elseif kwline == 2 " cpat matched
let format = 'context'
endif " If neither matched, keep default set above.
" let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000)
" let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000)
" if cline > 0
" let format = 'context'
" endif
" if lline > 0 && (cline == 0 || cline > lline)
" let format = 'tex'
" endif
endif " firstNC
call setpos('.', save_cursor)
endif " firstline =~ '^%&\s*\a\+'
" Translation from formats to file types. TODO: add AMSTeX, RevTex, others?
if format == 'plain'
setf plaintex
elseif format == 'context'
setf context
else " probably LaTeX
setf tex
endif
return
endfunc
This function determines which flavor of tex to use. If you look at it you can see that default value is taken from g:tex_flavor (if it exists, defaults to plain). If you set this variable to tex (in you vimrc), vim will default to the tex filetype.
let g:tex_flavor = 'tex'
There is a way to automate the process you're doing manually.
Adding autocmd BufRead,BufNewFile *.tex set filetype=tex to your .vimrc file will set tex filetype for each tex file, once you open them in vim.

How do I search the open buffers in Vim?

I'd like to search for text in all files currently open in vim and display all results in a single place. There are two problems, I guess:
I can't pass the list of open files to :grep/:vim, especially the names of files that aren't on the disk;
The result of :grep -C 1 text doesn't look good in the quickfix window.
Here is a nice example of multiple file search in Sublime Text 2:
Any ideas?
Or
:bufdo vimgrepadd threading % | copen
The quickfix window may not look good for you but it's a hell of a lot more functional than ST2's "results panel" if only because you can keep it open and visible while jumping to locations and interact with it if it's not there.
ack and Ack.vim handle this problem beautifully. You can also use :help :vimgrep. For example:
:bufdo AckAdd -n threading
will create a nice quickfix window that lets you hop to the cursor position.
Like the answer of Waz, I have written custom commands for that, published in my GrepCommands plugin. It allows to search over buffers (:BufGrep), visible windows (:WinGrep), tabs, and arguments.
(But like all the other answers, it doesn't handle unnamed buffers yet.)
I really liked romainl's answer, but there were a few sticky edges that made it awkward to use in practice.
The following in your .vimrc file introduces a user command Gall (Grep all) that addresses the issues that I found irksome.
funct! GallFunction(re)
cexpr []
execute 'silent! noautocmd bufdo vimgrepadd /' . a:re . '/j %'
cw
endfunct
command! -nargs=1 Gall call GallFunction(<q-args>)
This will allow case-sensitive searches like this:
:Gall Error\C
and case-insensitive:
:Gall error
and with spaces:
:Gall fn run
Pros
It will only open the Quickfix window, nothing else.
It will clear the Quickfix window first before vimgrepadd-ing results from each buffer.
The Quickfix window will contain the locations of all matches throughout the open buffers, not just the last visited.
Use :Gall repeatedly without any special housekeeping between calls.
Doesn't wait on errors and displays results immediately.
Doesn't allow any autocmd to run, speeding up the overall operation.
Ambivalent features
Doesn't preemptively jump to any occurrence in the list. :cn gets second result or CTRL-w b <enter> to get to the first result directly.
Cons
If there's only one result, you'll have to navigate to it manually with CTRL-w b <enter>.
To navigate to a result in any buffer quickly:
:[count]cn
or
:[count]cp
E.g. :6cn to skip 6 results down the list, and navigate to the correct buffer and line in the "main" window.
Obviously, window navigation is essential:
Ctrl-w w "next window (you'll need this at a bare minimum)
Ctrl-w t Ctrl-w o "go to the top window then close everything else
Ctrl-w c "close the current window, i.e. usually the Quickfix window
:ccl "close Quickfix window
If you close the Quickfix window, then need the results again, just use:
:cw
or
:copen
to get it back.
I made this function a long time ago, and I'm guessing it's probably not the cleanest of solutions, but it has been useful for me:
" Looks for a pattern in the open buffers.
" If list == 'c' then put results in the quickfix list.
" If list == 'l' then put results in the location list.
function! GrepBuffers(pattern, list)
let str = ''
if (a:list == 'l')
let str = 'l'
endif
let str = str . 'vimgrep /' . a:pattern . '/'
for i in range(1, bufnr('$'))
let str = str . ' ' . fnameescape(bufname(i))
endfor
execute str
execute a:list . 'w'
endfunction
" :GrepBuffers('pattern') puts results into the quickfix list
command! -nargs=1 GrepBuffers call GrepBuffers(<args>, 'c')
" :GrepBuffersL('pattern') puts results into the location list
command! -nargs=1 GrepBuffersL call GrepBuffers(<args>, 'l')
An improved (on steroids) version of Waz's answer, with better buffer searching and special case handling, can be found below (The moderators wouldn't let me update Waz's answer anymore :D).
A more fleshed out version with binds for arrow keys to navigate the QuickFix list and F3 to close the QuickFix window can be found here: https://pastebin.com/5AfbY8sm
(When i feel like figuring out how to make a plugin i'll update this answer. I wanted to expedite sharing it for now)
" Looks for a pattern in the buffers.
" Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]
" If pattern is not specified then usage instructions will get printed.
" If matchCase = '1' then exclude matches that do not have the same case. If matchCase = '0' then ignore case.
" If prefix == 'c' then put results in the QuickFix list. If prefix == 'l' then put results in the location list for the current window.
function! s:GrepBuffers(...)
if a:0 > 4
throw "Too many arguments"
endif
if a:0 >= 1
let l:pattern = a:1
else
echo 'Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]'
return
endif
let l:matchCase = 0
if a:0 >= 2
if a:2 !~ '^\d\+$' || a:2 > 1 || a:2 < 0
throw "ArgumentException: matchCase value '" . a:2 . "' is not in the bounds [0,1]."
endif
let l:matchCase = a:2
endif
let l:matchWholeWord = 0
if a:0 >= 3
if a:3 !~ '^\d\+$' || a:3 > 1 || a:3 < 0
throw "ArgumentException: matchWholeWord value '" . a:3 . "' is not in the bounds [0,1]."
endif
let l:matchWholeWord = a:3
endif
let l:prefix = 'c'
if a:0 >= 4
if a:4 != 'c' && a:4 != 'l'
throw "ArgumentException: prefix value '" . a:4 . "' is not 'c' or 'l'."
endif
let l:prefix = a:4
endif
let ignorecase = &ignorecase
let &ignorecase = l:matchCase == 0
try
if l:prefix == 'c'
let l:vimgrep = 'vimgrep'
elseif l:prefix == 'l'
let l:vimgrep = 'lvimgrep'
endif
if l:matchWholeWord
let l:pattern = '\<' . l:pattern . '\>'
endif
let str = 'silent ' . l:vimgrep . ' /' . l:pattern . '/'
for buf in getbufinfo()
if buflisted(buf.bufnr) " Skips unlisted buffers because they are not used for normal editing
if !bufexists(buf.bufnr)
throw 'Buffer does not exist: "' . buf.bufnr . '"'
elseif empty(bufname(buf.bufnr)) && getbufvar(buf.bufnr, '&buftype') != 'quickfix'
if len(getbufline(buf.bufnr, '2')) != 0 || strlen(getbufline(buf.bufnr, '1')[0]) != 0
echohl warningmsg | echomsg 'Skipping unnamed buffer: [' . buf.bufnr . ']' | echohl normal
endif
else
let str = str . ' ' . fnameescape(bufname(buf.bufnr))
endif
endif
endfor
try
execute str
catch /^Vim\%((\a\+)\)\=:E\%(683\|480\):/ "E683: File name missing or invalid pattern --- E480: No match:
" How do you want to handle this exception?
echoerr v:exception
return
endtry
execute l:prefix . 'window'
"catch /.*/
finally
let &ignorecase = ignorecase
endtry
endfunction

How to enumerate tabs in vim?

Vim is very productive editor and I enjoy using it everyday, but I've found that moving between tabs takes more time than it should.
When I want to switch to another tab I often repeat gt or gT multiple times. Vim provides a better way to reach required tab - n + gt, where n is tab number. But to use it you should count tab number first. It quickly become boring if you open a dozen of tabs.
I think it would be nice to enumerate tabs. A single number on each tab in front of file name, something like this:
1 Readme | 2 main.c | 3 main.h | 4 process.h
I hope it is possible to configure vim to do this by editing config or using some plugin.
Is there a way to achieve it?
You can use the tabline option for setting the label of the tabs in console mode of vim.
See the help at :h setting-tabline which also shows a very basic minimal example, which you can tweak to your need, e.g. for what you want, I would use something like this:
fu! MyTabLabel(n)
let buflist = tabpagebuflist(a:n)
let winnr = tabpagewinnr(a:n)
let string = fnamemodify(bufname(buflist[winnr - 1]), ':t')
return empty(string) ? '[unnamed]' : string
endfu
fu! MyTabLine()
let s = ''
for i in range(tabpagenr('$'))
" select the highlighting
if i + 1 == tabpagenr()
let s .= '%#TabLineSel#'
else
let s .= '%#TabLine#'
endif
" set the tab page number (for mouse clicks)
"let s .= '%' . (i + 1) . 'T'
" display tabnumber (for use with <count>gt, etc)
let s .= ' '. (i+1) . ' '
" the label is made by MyTabLabel()
let s .= ' %{MyTabLabel(' . (i + 1) . ')} '
if i+1 < tabpagenr('$')
let s .= ' |'
endif
endfor
return s
endfu
set tabline=%!MyTabLine()
If you are using gvim:
set guitablabel=(%N)\ %t\ %M
Type :help tabline and :help guitablabel to read more.
There is a function MyTabLine() in the doc.

Resources