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.
Related
I would like vim to display the total document word count in the status bar (where the current line and character number are displayed). I have come across similar questions on SO, and have tried all the suggestions mentioned here and here --- and none of them had any effect whatsoever on my status bar.
To explicitly name a few, I tried to paste any of the following in my ~/.vimrc (and ofc subsequently restarted vim):
function! CountNonEmpty()
let l = 1
let char_count = 0
while l <= line("$")
if len(substitute(getline(l), '\s', '', 'g')) > 3
let char_count += 1
endif
let l += 1
endwhile
return char_count
endfunction
function WordCount()
let s:old_status = v:statusmsg
exe "silent normal g\<c-g>"
let s:word_count = str2nr(split(v:statusmsg)[11])
let v:statusmsg = s:old_status
return s:word_count
endfunction
" If buffer modified, update any 'Last modified: ' in the first 20 lines.
" 'Last modified: ' can have up to 10 characters before (they are retained).
" Restores cursor and window position using save_cursor variable.
function! LastModified()
if &modified
let save_cursor = getpos(".")
let n = min([15, line("$")])
keepjumps exe '1,' . n . 's#^\(.\{,10}LOC:\).*#\1' .
\ ' ' . CountNonEmpty() . '#e'
keepjumps exe '1,' . n . 's#^\(.\{,10}Word Count:\).*#\1' .
\ ' ' . WordCount() . '#e'
call histdel('search', -1)
call setpos('.', save_cursor)
endif
endfun
OR
function WordCount()
let s:old_status = v:statusmsg
exe "silent normal g\<c-g>"
let s:word_count = str2nr(split(v:statusmsg)[11])
let v:statusmsg = s:old_status
return s:word_count
endfunction
set statusline=wc:%{WordCount()}
OR
function! WordCount()
let s:old_status = v:statusmsg
let position = getpos(".")
exe ":silent normal g\<c-g>"
let stat = v:statusmsg
let s:word_count = 0
if stat != '--No lines in buffer--'
let s:word_count = str2nr(split(v:statusmsg)[11])
let v:statusmsg = s:old_status
end
call setpos('.', position)
return s:word_count
endfunction
set statusline=wc:%{WordCount()}
OR
let g:word_count="<unknown>"
fun! WordCount()
return g:word_count
endfun
fun! UpdateWordCount()
let s = system("wc -w ".expand("%p"))
let parts = split(s, ' ')
if len(parts) > 1
let g:word_count = parts[0]
endif
endfun
augroup WordCounter
au! CursorHold * call UpdateWordCount()
au! CursorHoldI * call UpdateWordCount()
augroup END
" how eager are you? (default is 4000 ms)
set updatetime=500
" modify as you please...
set statusline=%{WordCount()}\ words
or many many more. And as I said there wa no efect. No error message, no visually perceptible change. I guess there may be a common issue which I am missing, but what is it?
Assuming your status line is enabled (set laststatus=2), the following:
set statusline+=%{wordcount().words}\ words
does exactly what you want in Vim version 7.4.1042 and above:
See :help wordcount().
If you absolutely need backward compatibility, the following is pretty much guaranteed to work in Vim 7.x, and will probably also work in earlier versions:
function! WC()
return len(split(join(getline(1,'$'), ' '), '\s\+'))
endfunction
set statusline+=%{WC()}\ words
Some of the answers from those old threads may be faster or smarter, though.
Your comments about those functions from those old threads not changing anything to your status line make me wonder if the problem is in all those old answers or elsewhere. Maybe… you don't have a status line to begin with?
Word count in vim-airline
Word count is provided standard by vim-airline for a number of file types, being at the time of writing:
asciidoc, help, mail, markdown, org, rst, tex ,text
If word count is not shown in the vim-airline, more often this is due to an unrecognised file type. For example, at least for now, the compound file type markdown.pandoc is not being recognised by vim-airline for word count. This can easily be remedied by amending the .vimrc as follows:
let g:airline#extensions#wordcount#filetypes = '\vasciidoc|help|mail|markdown|markdown.pandoc|org|rst|tex|text'
set laststatus=2 " enables vim-airline.
The \v statement overrides the default g:airline#extensions#wordcount#filetypes variable. The last line ensures vim-airline is enabled.
In case of doubt, the &filetype of an opened file is returned upon issuing the following command:
:echo &filetype
Here is a meta-example:
I currently write a substitute function that I often need for programming in vim.
The functions I already wrote look like this and run basically okay for searching and replacing strings which do not have any special characters inside. I already realized to escape the "/" automatically. My question is, how do I have to adapt the escape() function in the line
execute ':silent :argdo %s/' . escape(searchPattern, '/') . '/' . escape(replacePattern, '/') . '/ge'
So that automatically all of the characters that have to be escaped will be escaped?
" MAIN FUNCTION
" inspired by http://vimcasts.org/episodes/project-wide-find-and-replace/
function! SubstituteProjectwide(searchInput)
:set hidden
let cwd = getcwd()
let filename=expand('%:p')
call inputsave()
let searchPattern = input('Search for: ', a:searchInput)
let replacePattern = input('Replace "' . searchPattern . '" with: ')
let filePattern = input('Filepattern: ', cwd . '/**/*.*')
call inputrestore()
execute ':silent :args ' . filePattern
execute ':silent :vimgrep /' . searchPattern . '/g ##'
execute ':silent :Qargs'
execute ':silent :argdo %s/' . escape(searchPattern, '/\') . '/' . escape(replacePattern, '/\') . '/ge'
execute ':silent :edit ' . filename
echo 'Replaced "' . searchPattern . '" with "' . replacePattern . '" in ' . filePattern
endfunction
" VISUAL ENTRYPOINT WITH SELECTED TEXT
function! SubstituteProjectwideVisual()
let v = #*
call SubstituteProjectwide(GetVisualSelectedText())
endfunction
:vnoremap <F6> :call SubstituteProjectwideVisual()<cr>
" NORMAL ENTRYPOINT WIHT WORD UNDER CURSOR
function! SubstituteProjectwideNormal()
let wordUnderCursor = expand("<cword>")
call SubsituteProjectwide(wordUnderCursor)
endfunction
:nnoremap <F6> :call SubstituteProjectwideNormal()<cr>
" GETTING THE FILES WICH CONTAIN SEARCH PATTERN
" copied from http://vimcasts.org/episodes/project-wide-find-and-replace/
command! -nargs=0 -bar Qargs execute 'args' QuickfixFilenames()
function! QuickfixFilenames()
let buffer_numbers = {}
for quickfix_item in getqflist()
let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
endfor
return join(map(values(buffer_numbers), 'fnameescape(v:val)'))
endfunction
" GETTING THE CURRENT VISUAL SELECTION
" copied from: https://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript
function! GetVisualSelectedText()
let [line_start, column_start] = getpos("'<")[1:2]
let [line_end, column_end] = getpos("'>")[1:2]
let lines = getline(line_start, line_end)
if len(lines) == 0
return ''
endif
let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
let lines[0] = lines[0][column_start - 1:]
return join(lines, "\n")
endfunction
UPDATE
I managed to escape many characters like that
escape(searchPattern, ' / \') . '/' . escape(replacePattern, ' / \')
But how do I know which list of characters i have to escape, when it is basically possible, that every character can be inside the search and also the replace string?
To do a literal substitution, specify "very-nomagic" (:help /\V) , and escape the separator (/) and \
in the source.
In the replacement, & and ~ must be escaped, too, if the 'magic' option is set. (\V doesn't work here.)
execute ':silent :argdo %s/\V' . escape(searchPattern, '/\') . '/' . escape(replacePattern, '/\' . (&magic ? '&~' : '')) . '/ge'
Line breaks (if possible) must be changed from ^M to \n:
execute ':silent :argdo %s/\V' . substitute(escape(searchPattern, '/\'),"\n",'\\n','ge') . '/' . escape(replacePattern, '/\' . (&magic ? '&~' : '')) . '/ge'
This doesn't exactly answer your question but is another way of looking at the problem you're trying to solve. I don't entirely follow what the :args setup is doing for you since the quickfix has all of the info you need after the :vimgrep.
I have this in my vimrc:
nnoremap <F3> :vimgrep // $PROJECT_ROOT_DIR/src/**/*.{cpp,h,c,inl,msg}<C-Left><C-Left><Right>
Obviously you'll want to customize the search path, as this focuses on just the above five file extensions in a specific file hierarchy that was configured each time I launched Vim...
Anyway, once you've got that, :cr makes sure you're at the beginning, then do the search&replace you want inside of a macro. You can actually test it out on the first few finds if you want, but then...
qbq Clear the 'b' register.
qa Start recording the 'a' macro'
:%s/this/that/g Start the macro and substitute 'that' for 'this'. (Press enter)
:w|cnf write the file and go to the next one (Press enter)
q Stop recording the 'a' macro.
Then qb#a#bq will run the macro once, saving it in #b. Then just run
(type) #b once more and it'll keep calling itself until it's done.
I'm trying to write a function that replaces text in all buffers. So I call Ack to search all the matches and next step I want to set into Quickfix command line this code
:QuickFixDoAll %s/foo/boo/gc
Seems like I can only call 'exec' function which runs this command immediately and there is no ablility to edit it or cancel at all
I also tried "input" function to read user input but got this error at runtime
not an editor command
Any ideas?
.vimrc:
function! ReplaceInFiles(o, n)
exec "Ack '" . a:o . "'"
exec "QuickFixDoAll %s/" . a:o . "/" . a:n . "/gc"
endfunction
" QuickFixDoAll
function! QuickFixDoAll(command)
if empty(getqflist())
return
endif
let s:prev_val = ""
for d in getqflist()
let s:curr_val = bufname(d.bufnr)
if (s:curr_val != s:prev_val)
exec "edit " . s:curr_val
exec a:command
endif
let s:prev_val = s:curr_val
endfor
exec "quit"
endfunction
command! -nargs=+ QuickFixDoAll call QuickFixDoAll(<f-args>)
Using input()
This queries both values interactively:
function! ReplaceInFiles()
let l:o = input('search? ')
let l:n = input('replace? ')
exec "Ack '" . l:o . "'"
exec "QuickFixDoAll %s/" . l:o . "/" . l:n . "/gc"
endfunction
nnoremap <Leader>r :call ReplaceInFiles()<CR>
Incomplete mapping
nnoremap <Leader>r :let o = ''<Bar>exec "Ack '" . o . "'"<Bar>exec "QuickFixDoAll %s/" . o . "//gc"<Home><Right><Right><Right><Right><Right><Right><Right><Right><Right>
This one puts the cursor on the right spot for the search. As this value is used twice (Ack and QuickFixDoAll), it is assigned to a variable. After that, move to the end of the command and fill in the replacement in between the //gc.
Custom parsing
The most comfortable option would be a custom command :AckAndSubstAll/search/replacement/. For that, you'd need to parse the two parts in the custom command (like :s does). You could do that with matchstr(), or use ingo#cmdargs#substitute#Parse() from my ingo-library plugin.
First use vim-qargs to copy all files from the quickfix window into Vim's arglist by calling :Qargs.
Then run your replace on all arguments in the arglist by doing :argdo %s/search/replace/gc
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
I'm using Vim for all program editing and I have a standard header I use at the top of all my source code files. I have a .vimrc file set up to update certain fields in this header (like Last Modified) when I save any changes using :w
My question is, how do I put in a function to count lines of code, following the basic rule that only non-blank lines are counted?
I know within an open vim buffer, I can use
:%s/\n//gn
to count all lines, and
:%s/\n\n//gn
to count blank lines (basically count how many times two newlines appear in a row, indicating a blank line). But how do I put this in my .vimrc file?
Here's the code fragment from my .vimrc that updates the header fields:
function! LastModified()
if &modified
let save_cursor = getpos(".")
let n = min([20, line("$")])
keepjumps exe '1,' . n . 's#^\(.\{,10}Last Modified:\).*#\1' .
\ strftime(' %a %b %d, %Y %I:%M%p') . '#e'
keepjumps exe '1,' . n . 's#^\(.\{,10}Filename:\).*#\1' .
\ ' ' . #% . '#e'
keepjumps exe '1,' . n . 's#^\(.\{,10}LOC:\).*#\1' .
\ ' ' . '' . '#e'
call histdel('search', -1)
call setpos('.', save_cursor)
endif
endfun
Also, I would just like to add, I know there are numerous other ways to do this (like using wc --lines from the shell) but I'm interested in learning how to really configure my editor (so call it a learning exercise).
You actually should not want to use :s here:
function! CountNonEmpty()
return len(filter(getline(1, line('$')), '!empty(v:val)'))
endfunction
By the way, I would have used getline+map+setline to implement your header updater:
function! LastModified()
if &modified
" If number of buffer lines is < 20, then getline(1, 20)"
" will return only existing lines without any errors "
call setline(1, map(getline(1, 20), 'substitute(substitute(substitute(v:val, '.
\'"^\\v(.{,10}Last Modified:).*", "\\1 ".strftime("%s %b %d, %Y %I:%M%p"), ""),'.
\'"^\\v(.{,10}Filename:).*", "\\1 ".escape(#%, "&\\~"), ""),'.
\'"^\\v(.{,10}LOC:).*", "\\1 ", "")'))
endif
endfunction
This might help:
function! CountNonEmpty()
redir => g:nonblank
silent %s/^.\+$/&/n
redir END
return substitute(g:nonblank, '\n\s*\(\d\+\)\D.*$', '\1', '')
endfunction
:redir => Stores the output of the following ex commands into the given variable. See :help :redir