Quickly switching between a file and a test file in vim - 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.

Related

Trigger file completion from vimscript

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

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/*

How to use execut in vim indentexpr

I tried to use execut in vim indentexpr with under code
function! AddSpace(lnum,str)
while len(getline(a:lnum)) < 80
execut a:lnum . "," . a:lnum . "s/".a:str."/ ".a:str
endwhile
endfunction
function! GetIndent()
if getline(v:lnum) =~ ';'
call AddSpace(v:lnum,";")
endif
...
return ...
endfunction
setlocal indentexpr=GetIndent()
and gg=G.It doesn't work...
Vim just fail into a dead loop...
However,other indent rules works before the loop.
But hen I call it by
call AddSpace(3,";")
It works fine.
Maybe "execut" dosen't work in indentexpr?
Is there still any way to insert space into the file with "execut"?
Or is there better way to finish the insert without cursor moving?
Thanks for your help!
'indentexpr' is not meant to actually modify the text directly. In fact, it is explicitly forbidden to modify the text while evaluating the expression. Error messages are suppressed by default, so you don't get an indication something is wrong, but the expression just stops when it encounters the text modification.
Your AddSpace function should return the number of spaces to add, rather than actually adding the spaces. Note you don't need the while loop, you can just use subtraction to find the number of spaces needed.
See :help 'indentexpr' for details.

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.

Vim line completion with external file

Can line completion Ctrl+X Ctrl+L be used to show line completions from a specific external file instead of "only" from the current buffer?
Something like dictionaries, but for lines.
Update:
To test I did following:
created a file tt.txt with some test lines
placed the file in D:\t1\ (I'm on windows)
included the file with :set path+=D:\\t1\\tt.txt
:set complete ? returns complete =.,w,b,u,t,i
:set path ? returns path=.,,,D:\t1\tt.txt
checkpath returns: All included files were found
typing a line which should be completed with the matching content from tt.txt with Ctrl+X Ctrl+L returns pattern not found
What am I missing?
I think the only way to achieve what you want is with a custom complete-function. See help complete-functions for the (very useful!) documentation. Here's my attempt at a solution:
First you need a separate function to silently grep a file for a string (if you just call the naked vimgrep function you will get an ugly error if there are no matches).
function! SilentFileGrep( leader, file )
try
exe 'vimgrep /^\s*' . a:leader . '.*/j ' . a:file
catch /.*/
echo "no matches"
endtry
endfunction
Now, here's your completion function. Note that the path to the file you want to search is hard-coded in here, but you could change it to use a variable if you so wish. We call SilentFileGrep(), which dumps the results in the quickfix list. Then we extract the results from the qflist (trimming the leading whitespace) and clear the qflist before returning the results.
function! LineCompleteFromFile(findstart,base)
if a:findstart
" column to begin searching from (first non-whitespace column):
return match(getline("."),'\S')
else
" grep the file and build list of results:
let path = <path_to_file>
call SilentFileGrep( a:base, path )
let matches = []
for thismatch in getqflist()
" trim leading whitespace
call add(matches, matchstr(thismatch.text,'\S.*'))
endfor
call setqflist([])
return matches
endif
endfunction
To use this function, you need to set the completefunc option to point at it:
set completefunc=LineCompleteFromFile
Then you can use <C-X><C-U> to invoke the completion, which you could easily map to <C-X><C-L>.
This seems to work pretty well for me, but it is not exhaustively tested.
In Vim help for line completion it's written that it only works for loaded buffers. As a workaround, you may open your "dictionary" file in another buffer and Vim will suggest lines from this buffer.
'path' is supposed to be a list of directories so (assuming that's the correct syntax on Windows) :set path+=D:\\t1\\tt.txt should be :set path+=D:\\t1\\ or :set path+=D:\\t1.
The i in 'complete' means that the completion candidates are chosen from current and included files. You must include a file for completion to work: it won't if you don't explicitly include that file.
Say that you have created ~/test/testfile with this single line:
lorem ipsum dolor sit amet
You add it to Vim's 'path' with:
:set path+=~/test
To use it as completion source in a C++ file you would do:
#include <testfile>
and be able to do:
lore<C-x><C-f>
to get:
lorem ipsum dolor sit amet
As far as I know, it doesn't work with languages that don't have an include mechanism like C or C++ so you can forget about it for Markdown, JavaScript, plain text or, if my tests are any indication, even PHP which does have include.
If you want a more generic mechanism, just add that file to the arglist: it will automatically be used as a completion source:
:argadd ~/test/testfile
If you do
:set dictionary=<some file>
then you can use ctrl + x followed by ctrl + k to complete from <some file>
See
:help ins-completion
for more info.
As #black_wizard points out, the other file must be loaded in a buffer. With set hidden, you can use the following to load another file and return to the previous buffer:
command! -nargs=? XL silent edit <args> | silent bprevious
To load tt.txt into another buffer and return to the previous one:
:XL tt.txt

Resources