" Query Command Complete " ====================== " " Vim plugin to suggest completions with the results of an external " query command. " " The original intention is to use it as a mutt query_command wrapper " to complete addresses in the mail headers, but it can be adapted " to any other kind of functionality by modifying the exposed setting " parameters. " " Last Change: 2013 Jun 16 " Author: Caio Romão (http://caioromao.com) " License: This file is placed in the public domain " Contributors: " Brian Henderson https://github.com/bhenderson " Mark Stillwell https://github.com/marklee77 " Rui Abreu Ferreira http://ruiabreu.org " " Setup: " This plugin exports the completion function QueryCommandComplete, " which can be set as the complete function (or omni function) for " any filetype. If you have a working mutt setup with query_command " configured, the plugin works out of the box. " " Example: " let g:qcc_query_command='abook' " au BufRead /tmp/mutt* setlocal omnifunc=QueryCommandComplete " " Settings: " Note: Overriding settings on a buffer-basis is supported. So " b:qcc_query_command takes precedence over g:qcc_query_command " " g:qcc_query_command " External command that queries for contacts " If empty, QueryCommandComplete tries to guess what command to " run by executing `mutt -Q query_command`. " " g:qcc_line_separator " Separator for each entry in the result from the query " default: '\n' " " g:qcc_field_separator " Separator for the fields of an entry from the result " default: '\t' " " g:qcc_pattern " Pattern used to match against the current line to decide " whether to call the query command " default: '^\(To\|Cc\|Bcc\|From\|Reply-To\):' " " g:qcc_multiline " Whether to try matching g:qcc_pattern against the current " and any previous line " default: 0 " " g:qcc_multiline_pattern " Pattern to match against the current line when deciding " wether to keep looking for a line that matches g:qcc_pattern " This provides finer control over the recursion, which " is useful if calling the completion on really big files. " default: '.*' " " g:qcc_format_word " Format string to be used when building the word field " of the completion (i.e.: the final result, what gets fed into " the current line when you select an option) " default: '${1} <${0}>' (as in: FirstName ) " " g:qcc_format_abbr " Format string to be used when building the abbreviation " for the completion menu (i.e.: the first row in the completion " menu). " default: '${1}' " " g:qcc_format_menu " Format string for the optional second column of the completion " menu. " default: '${2}' " if exists("g:loaded_QueryCommandComplete") || &cp finish endif function! s:GetSetting(name) let global_option = 'g:qcc_' . a:name let local_option = 'b:qcc_' . a:name let result = '' if exists(local_option) let result = {local_option} elseif exists(global_option) let result = {global_option} endif return result endfunction let g:loaded_QueryCommandComplete = 1 let s:save_cpo = &cpo set cpo&vim function! s:DefaultIfUnset(name, default) if !exists(a:name) let {a:name} = a:default endif endfunction call s:DefaultIfUnset('g:qcc_line_separator', '\n') call s:DefaultIfUnset('g:qcc_field_separator', '\t') call s:DefaultIfUnset('g:qcc_pattern', '^\(To\|Cc\|Bcc\|From\|Reply-To\):') call s:DefaultIfUnset('g:qcc_multiline', 0) call s:DefaultIfUnset('g:qcc_multiline_pattern', '.*') call s:DefaultIfUnset('g:qcc_format_word', '${1} <${0}>') call s:DefaultIfUnset('g:qcc_format_abbr', '${1} <${0}>') call s:DefaultIfUnset('g:qcc_format_menu', '${2}') " Given a format string where the placeholders are in the format " '${index}' and index is a valid index the the given 'fields' " argument, this function returns a string with all placeholders " replaced by the corresponding data in the fields list. " FIXME I can't help but think there's a standard way to do this " but I failed finding it. Please call me a dumbass if you " know The Easy Way. function! s:ApplyFieldsToFormatString(fields, format) let result = a:format while 1 let placeholder = matchstr(result, '${[0-9]}') if (empty(placeholder)) break endif let index = matchstr(placeholder, '[0-9]') " If ${NUMBER} is not a valid index in a:fields, " use '' as a fallback. " FIXME Decide whether to warn/err/whatever here let content = '' if (len(a:fields) > index) let content = a:fields[index] endif let result = substitute(result, placeholder, content, 'g') endwhile return result endfunction function! s:MakeCompletionEntry(fields) let entry = {} let entry.word = s:ApplyFieldsToFormatString(a:fields, s:GetSetting('format_word')) let entry.abbr = s:ApplyFieldsToFormatString(a:fields, s:GetSetting('format_abbr')) let entry.menu = s:ApplyFieldsToFormatString(a:fields, s:GetSetting('format_menu')) let entry.icase = 1 return entry endfunction function! s:FindStartingIndex() let cur_line = getline('.') " locate the start of the word let start = col('.') - 1 while start > 0 && cur_line[start - 1] =~ '[^:,]' let start -= 1 endwhile " lstrip() while cur_line[start] =~ '[ ]' let start += 1 endwhile return start endfunction function! s:GenerateCompletions(findstart, base) if a:findstart return s:FindStartingIndex() endif let results = [] let cmd = s:GetSetting('query_command') if cmd !~ '%s' let cmd .= ' %s' endif let cmd = substitute(cmd, '%s', shellescape(a:base), '') let lines = split(system(cmd), g:qcc_line_separator) for my_line in lines let fields = split(my_line, g:qcc_field_separator) let entry = s:MakeCompletionEntry(fields) call add(results, entry) endfor return results endfunction function! s:ShouldGenerateCompletions(line_number) let current_line = getline(a:line_number) if current_line =~ g:qcc_pattern return 1 endif if ! g:qcc_multiline || a:line_number <= 1 || current_line !~ g:qcc_multiline_pattern return 0 endif return s:ShouldGenerateCompletions(a:line_number - 1) endfunction function! s:CheckSettings() " Try to use mutt's query_command by default if nothing is set if empty(s:GetSetting('query_command')) let s:querycmd = system('mutt -Q query_command 2>/dev/null') let s:querycmd = substitute(s:querycmd, '^query_command="\(.*\)"\n', '\1','') if len(s:querycmd) let g:qcc_query_command = s:querycmd let g:qcc_multiline = 1 autocmd FileType mail setlocal omnifunc=QueryCommandComplete else echoerr "QueryCommandComplete: g:qcc_query_command not set!" return 0 endif endif return 1 endfunction function! QueryCommandComplete(findstart, base) if s:CheckSettings() && s:ShouldGenerateCompletions(line('.')) return s:GenerateCompletions(a:findstart, a:base) endif endfunction let &cpo = s:save_cpo