Trigger file completion from vimscript - vim

probably the answer to my question is obvious but even after a straight our of searching I cannot find anything useful.
I'm currently writing a small vim latex auto-completion plugin that suggests completions based on the editing context. The relevant part of the code looks like this:
function! Complete_latex(findstart, base)
if a:findstart
" locate the start of the base
"....
else
if s:envname_required()
return s:env_complete(a:base)
endif
if s:citation_required()
return s:cite_complete(a:base)
endif
if s:filename_required()
" TODO: Trigger filename completion
endif
endif
endfunction
set omnifunc=Complete_latex
The *_required() functions basically throw a bunch of regexps at the current line I'm editing to figure out what I'm doing right now. So if I am in INSERT mode at a position like ...\input{|... I'd like my omnifunc to call the same completion I can trigger with C-X C-F in INSERT mode.
As I also use the YouCompleteMe plugin and set { as a trigger for semantic completion in *.tex files, the triggering is being take care of.
I know that I can get a list of files and fill the popup menu myself, but I was nevertheless wondering If I can use a builtin function of vim.
Thank you.

I'm not entirely sure if that is the best way to go, but I came up with
let l:glob_pattern = a:base . '*'
let l:files_pre = globpath('.', l:glob_pattern, 0, 1)
let l:files_post = []
for l:file in l:files_pre
call add(l:files_post, substitute(l:file, '^\./', '', ''))
endfor
return l:files_post
Which basically gets all files in the current directory matching "base*" and returns a list of them. The post processing part just removes the './' at the beginning of each filename returned by globpath

Related

Quickly switching between a file and a test file in vim

Suppose I'm editing
src/a/b/c/d.c
and I expect a test file for this file to be in
test/a/b/c/d.c.c
how can I alternate between files following this pattern quickly?
a.vim and my alternate-lite fork support a searchpath option where you could specify how we can (quickly) switch between directories.
They're more tuned to jump between a header file and a definition file, but it should be possible to add test files as well -- I don't know how it'd behave with .c.c VS .c actually.
Given the pattern you've given us, the vanilla (non scalable) approach would be something like (untested):
function! s:alt_name(name) abort
if a:name =~ '\.c\.c$'
return substitute(a:name, '\v<test>/(.*)\.c', 'src/\1', '')
elseif a:name =~ '\.c$'
return substitute(a:name, '\v<src>/(.*\.c)', 'test/\1.c', '')
else
return a:name
endif
endfunction
command! -nargs=0 Switch :exe ':e '.s:alt_name(expand('%'))
Of course, if you need to jump to a window where the buffer is already opened, or split, or... well. That's why there are plugins.

Using Vim in a GTD way

I'd like to change my habit in the way I take notes.
I want add files named YYYYmmddHHiiss.txt in a directory and start them this way:
=Call somebody (title of my note)
#work (context of my note)
!todo (type of the note, I'll use !inbox, !todo, !waiting, !someday and !reference, each one his habit)
#project_name
#call
#Name of the person
#other tags if needed...
Details...
What I'd like is:
Using Vim (no plugins, just built-in features; no external programs; just a few autocmd, mappings and functions in my personnal vimrc)
Saving all my notes in a single directory and trust Vim and my tags to find what I need.
Start using this system with one command of this kind :GtdGrep and think in a while if I need more.
Model
:GtdGrep !todo #work
:GtdGrep !inbox
:GtdGrep #waiting #home
:GtdGrep !todo #work #this_project
:GtdGrep #name_of_a_co-worker #this_project
Now that I introduced you my need, I can start describing my problem ^^ I want to create the function behind the :GtdGrep command but there is a lot of things I don't manage to gather... Here is my draft.
let gtd_dir=expand($HOME)."/Desktop/notes"
function! GtdGrep(...)
execute "silent! vimgrep /\%<10l".join(a:000, "\\_.*")."/j ".gtd_dir."/**"
execute "copen"
endfunction
command! -nargs=* GtdGrep call GtdGrep(<f-args>)
How to restrain the search before the first empty line? I managed to look for my tags in the first 9 lines with the regexp \%<10l but that's it.
How to look for my tags regardless of their positions in the file? I just succeeded to do the grep on several lines with the \_.* regexp which is for the line returns.
The icing on the cake will be that the display on the quickfix window focus on the title part of my note (after /^=). I think it is possible with a ^=\zs.*\ze but it is too much for me in a single vimgrep!
EDIT
I solve my "AND" vimgrep issue by doing successive vimgrep on the previous results. Is it a good solution?
let gtd_dir=expand($HOME)."/Desktop/notes"
function! GtdGrep(...)
let dest = g:gtd_dir."/**"
for arg in a:000
execute "silent! vimgrep /^".arg."$/j ".dest
let dest = []
let results = getqflist()
if empty(results)
break
else
for res in results
call add(dest, bufname(res.bufnr))
endfor
let dest = join(dest, ' ')
endif
endfor
" Last vimgrep to focus on titles before displaying results
let results = getqflist()
if !empty(results)
echom dest
execute "silent! vimgrep /\\%<10l^=.*/j ".dest
execute "copen"
else
echom "No results"
endif
endfunction
command! -nargs=* GtdGrep call GtdGrep(<f-args>)
I'd like to restrain my vimgrep on the lines before the first blank line but I didn't succeed to do this. Any idea?
First of all, you should know the risk if you use dynamic string as pattern. E.g. your project_name contains [].*()...
What you can try is, building this command:
vimgrep '/\%<10l\(foo\|bar\|blah\|whatever\)' path/*

Using abbreviation to insert comment

I’m trying to set up an abbreviation in my .vimrc that will insert a comment template for heading-level comments in my CSS files.
The comment I want to insert is:
/* ==========================================================================
#
========================================================================== */
I will then jump back to the # and add my title there (e.g. BUTTONS).
The abbreviation I have attempted to set up looks like this:
iab comsec·
\/* ==========================================================================
\<Cr>#
\<Cr>========================================================================== */
(Where · represents a trailing space.)
Right away this feels pretty crude, but the specific problem is that if try and drop a comsec in my CSS, it starts wrapping it in more comments. The output looks like this:
/* ==========================================================================
* #
* ========================================================================== */
Notice the two * at the beginnings of lines 2 and 3?
Is there a way to tell vim not to try and be clever and to just drop in exactly what I’ve told it? A way to prevent vim from trying to wrap comments around the comment?
I’m not a particularly hardcore vim user, so there’s every chance I’m overcomplicating things, or missing something obvious, or using the wrong tool for the job.
Thanks!
If you are the type of person who can keep track of your personal utilities, this isn't so fancy but works. You can import the output of an external command into your buffer, so I put a mapping like this in my .vimrc file:
"bc = block comment
map ,bc :read! python ~/my_personal_utils/insert_css_comment.py
So, I just have to type ",bc" to add the comment. On my Mac at least, this leaves me hanging in command mode, so that my cursor is after '.py' and I can quickly type an argument like BUTTONS (i.e. the python script takes an optional argument).
Here is a function to do that.
:function! Comment()
:normal! i/*
:normal! 80a=
:normal! o#
:normal! o
:normal! 80i=
:normal! a*/
:endfunction
You can put this in vimrc file and create a command or map for this.
Command
:cmap comsec call Comment()
You can keep the cursor on a line and then call this command.
Or an in insert mode mapping
:imap comsec <ESC>:comsec<CR>
As alternatives I'd suggest nerdcommenter for commenting/uncommenting with a few key strokes.
Or, even better, ultisnips. In which you can easily make your own template for those headings:
open a .css file
exec command : UltiSnipsEdit
create your own snip:
snippet heading "heading comments"
/* ===================================
* ${1}
* =================================== */
endsnippet
Here is a better and simple way to to insert your comment which check everytime if the line is surrounded by the comment template.
All you have to do is to write your comment on new line and then press ; during the insert mode. (you can change the character ; by
any combination you want.)
The comment template is set by the variable l:start, l:begin, l:end
so you can change the number of spaces or = as you like.
If you would like to change the template completely keep in mind that you need to change also the regular expressions for the variables l:matchprev, l:matchhier, l:matchnext .
inoremap <silent> ; <Esc>mx:call Comment()<cr>i<Right>
function! Comment()
let l:start= "/* ====="
let l:begin=" # "
let l:end= " ==== */"
let l:next=getline(line(".")+1)
let l:hier=getline(line("."))
let l:prev=getline(line(".")-1)
let l:matchnext= matchstr( l:next , '^\s*=\+\s*\*/\s*$')
let l:matchhier= matchstr( l:hier , '^\s*#\s*.*$')
let l:matchprev= matchstr( l:prev , '^\s*/\*\s*=\+\s*$')
if l:matchnext != '' && l:matchprev != '' && l:matchhier != ''
return 0
else
execute ":s:^.*$:".l:start."\r".l:begin."&\r".l:end."\r:"
"the number 3 is the length of the variable l:begin
normal! `xj3l
endif
endfunction
write this code in another file scriptname and then you can use the mapping in any css file by typing in the command mode first :so scriptname
Another alternative is to put all that simply in your .vimrc file

Function already exists VIM

I have defined a function in my /.vim/ftplugin/python.vim file. The problem is that every time I open a .py file, I get the E122: Function MyFunction already exists, add ! to replace it.
I know that if I add ! then it will override the function (which is not a problem), but that means that it will replace it everytime, and it is a useless (and not very clean) supplementary action.
I guess that the problem come from the Python configuration file being sourced again and again every time I open a new .py file.
How can I tell VIM to source only once?
I would recommend putting the function in an autoload directory. (Read :help autoload it does a very good job explaining how this works). The quick version is below.
Edit the file ~/.vim/autoload/ftplugin/python.vim and add your function there. Everything after the autoload is part of the function signiture. (Instead of / use # between directories and leave off the .vim for the filename directory(s)#file#FunctionName)
function ftplugin#python#MyFunction()
...
endfunction
This function will automatically be loaded by vim the first time it is used.
Inside the filetype plugin you would just create the necessary mappings and commands.
command -buffer MyFunction call ftplugin#python#MyFunction()
nnoremap <buffer> <leader>m :call ftplugin#python#MyFunction()<CR>
and the function will automatically be loaded when it is called the first time. And other buffer that loads the ftplugin won't run into the redefinition problem.
One way: define a variable at the end of the file, check for its existence at the beginning (similar to a c include guard):
if exists('g:my_python')
finish
endif
fun MyFunction
...
endfun
" ... other stuff
let g:my_python = 1
Another way (if all you have is this function): check directly for the existence of its definition:
if !exists('*MyFunction')
fun MyFunction
...
endfun
endif
If you use ultisnips plugin would be great to have a snippet like:
snippet guard "add guard to functions" b
if !exists('*`!p
try:
func_name = re.search('\S+\s+(\S+)\(', snip.v.text.splitlines()[0]).group(1)
except AttributeError:
func_name = ''
snip.rv = func_name
`')
${VISUAL}
endif
${0:jump here <C-j>}
endsnippet
It allow us to select a function with vip, trigger the guard snippet and fix
any function with no effort. In the post quoted you can see a complete explanation about the code above
It came from a discussion on vim #stackexchange. Actually I already knew about !exists thing, so I was trying to create a snippet to make my snippets smarter.

Trojan in vim's latex_suite?

I was going through some code for latex_suite called vim_latex (http://vim-latex.sourceforge.net/) and I found few interesting lines in the file called "templates.vim":
" Back-Door to trojans !!!
function! <SID>Compute(what)
exe a:what
if exists('s:comTemp')
return s:comTemp.s:comTemp
else
return ''
endif
endfunction
Well, I'm not an expert on vim code, so I cannot interpret these lines except for the comment that freak me up a bit. Do you guys have an idea about what is happening ?
Edit:
The function seems to be called only by the following one:
" ProcessTemplate: processes the special characters in template file. {{{
" This implementation follows from Gergely Kontra's
" mu-template.vim
" http://vim.sourceforge.net/scripts/script.php?script_id=222
function! <SID>ProcessTemplate()
if exists('s:phsTemp') && s:phsTemp != ''
exec 'silent! %s/^'.s:comTemp.'\(\_.\{-}\)'.s:comTemp.'$/\=<SID>Compute(submatch(1))/ge'
exec 'silent! %s/'.s:exeTemp.'\(.\{-}\)'.s:exeTemp.'/\=<SID>Exec(submatch(1))/ge'
exec 'silent! g/'.s:comTemp.s:comTemp.'/d'
" A function only puts one item into the search history...
call Tex_CleanSearchHistory()
endif
endfunction
According to the header file description, the aim of these functions is to handle templates located into a specific directory.
I think the comment is intended as a warning. The function <SID>ProcessTemplate() goes through a template file, looks for certain (configurable) patterns, and calls <SID>Compute(what) where the argument what is text extracted from the template. Note the line :exe a:what.
If you install a template file from an untrusted source, then bad things can happen.
Of course, if you install a vim plugin from an untrusted source, equally bad things can happen. Putting malware in a template file adds a few levels of indirection, making it harder to implement and harder to diagnose.
It is possible that this code was written before the :sandbox command was added to vim, and that might be an easy way to make this code safer. I have not looked at what is allowed in the sandbox and compared it to the intended use of this template processing.

Resources