"============================================================================= " FILE: buffer.vim " AUTHOR: Shougo Matsushita " License: MIT license {{{ " Permission is hereby granted, free of charge, to any person obtaining " a copy of this software and associated documentation files (the " "Software"), to deal in the Software without restriction, including " without limitation the rights to use, copy, modify, merge, publish, " distribute, sublicense, and/or sell copies of the Software, and to " permit persons to whom the Software is furnished to do so, subject to " the following conditions: " " The above copyright notice and this permission notice shall be included " in all copies or substantial portions of the Software. " " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS " OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " }}} "============================================================================= let s:save_cpo = &cpo set cpo&vim " Global options definition. "{{{ let g:neocomplete#sources#buffer#cache_limit_size = \ get(g:, 'neocomplete#sources#buffer#cache_limit_size', 500000) let g:neocomplete#sources#buffer#disabled_pattern = \ get(g:, 'neocomplete#sources#buffer#disabled_pattern', '') let g:neocomplete#sources#buffer#max_keyword_width = \ get(g:, 'neocomplete#sources#buffer#max_keyword_width', 80) "}}} " Important variables. if !exists('s:buffer_sources') let s:buffer_sources = {} let s:async_dictionary_list = {} endif let s:source = { \ 'name' : 'buffer', \ 'kind' : 'manual', \ 'mark' : '[B]', \ 'rank' : 5, \ 'min_pattern_length' : \ g:neocomplete#auto_completion_start_length, \ 'hooks' : {}, \ 'is_volatile' : 1, \} function! s:source.hooks.on_init(context) abort "{{{ let s:buffer_sources = {} augroup neocomplete "{{{ autocmd BufEnter,BufRead,BufWinEnter,BufWritePost * \ call s:check_source() autocmd InsertEnter,InsertLeave * \ call neocomplete#sources#buffer#make_cache_current_line() autocmd VimLeavePre * call s:clean() augroup END"}}} " Create cache directory. call neocomplete#cache#make_directory('buffer_cache') call neocomplete#cache#make_directory('buffer_temp') " Initialize script variables. "{{{ let s:buffer_sources = {} let s:async_dictionary_list = {} "}}} call s:make_cache_buffer(bufnr('%')) call s:check_source() endfunction "}}} function! s:source.hooks.on_final(context) abort "{{{ silent! delcommand NeoCompleteBufferMakeCache let s:buffer_sources = {} endfunction"}}} function! s:source.hooks.on_post_filter(context) abort "{{{ " Filters too long word. call filter(a:context.candidates, \ 'len(v:val.word) < g:neocomplete#sources#buffer#max_keyword_width') endfunction"}}} function! s:source.gather_candidates(context) abort "{{{ call s:check_async_cache(a:context) let keyword_list = [] for source in s:get_sources_list(a:context) let keyword_list += source.words endfor return keyword_list endfunction"}}} function! neocomplete#sources#buffer#define() abort "{{{ return s:source endfunction"}}} function! neocomplete#sources#buffer#get_frequencies() abort "{{{ return get(get(s:buffer_sources, bufnr('%'), {}), 'frequencies', {}) endfunction"}}} function! neocomplete#sources#buffer#make_cache_current_line() abort "{{{ if neocomplete#is_locked() return endif " let start = reltime() call s:make_cache_current_buffer( \ max([1, line('.') - winline()]), \ min([line('$'), line('.') + winheight(0) - winline()])) " echomsg reltimestr(reltime(start)) endfunction"}}} function! s:should_create_cache(bufnr) " {{{ let filepath = fnamemodify(bufname(a:bufnr), ':p') return getfsize(filepath) < g:neocomplete#sources#buffer#cache_limit_size \ && getbufvar(a:bufnr, '&modifiable') \ && !getwinvar(bufwinnr(a:bufnr), '&previewwindow') \ && (g:neocomplete#sources#buffer#disabled_pattern == '' \ || filepath !~# g:neocomplete#sources#buffer#disabled_pattern) endfunction"}}} function! s:get_sources_list(context) abort "{{{ let filetypes_dict = {} for filetype in a:context.filetypes let filetypes_dict[filetype] = 1 endfor return values(filter(copy(s:buffer_sources), \ "has_key(filetypes_dict, v:val.filetype) \ || has_key(filetypes_dict, '_') \ || bufnr('%') == v:key \ || (bufname('%') ==# '[Command Line]' && bufwinnr('#') == v:key)")) endfunction"}}} function! s:initialize_source(srcname) abort "{{{ let path = fnamemodify(bufname(a:srcname), ':p') let filename = fnamemodify(path, ':t') if filename == '' let filename = '[No Name]' let path .= '/[No Name]' endif let ft = getbufvar(a:srcname, '&filetype') if ft == '' let ft = 'nothing' endif let keyword_pattern = neocomplete#get_keyword_pattern(ft, s:source.name) let s:buffer_sources[a:srcname] = { \ 'words' : [], \ 'frequencies' : {}, \ 'name' : filename, 'filetype' : ft, \ 'keyword_pattern' : keyword_pattern, \ 'cached_time' : 0, \ 'path' : path, \ 'cache_name' : neocomplete#cache#encode_name('buffer_cache', path), \} endfunction"}}} function! s:make_cache_file(srcname) abort "{{{ " Initialize source. if !has_key(s:buffer_sources, a:srcname) call s:initialize_source(a:srcname) endif let source = s:buffer_sources[a:srcname] if !filereadable(source.path) \ || getbufvar(a:srcname, '&modified') \ || getbufvar(a:srcname, '&buftype') =~ 'nofile\|acwrite' call s:make_cache_buffer(a:srcname) return endif call neocomplete#print_debug('make_cache_buffer: ' . source.path) let source.cache_name = \ neocomplete#cache#async_load_from_file( \ 'buffer_cache', source.path, \ source.keyword_pattern, 'B') let source.cached_time = localtime() let source.filetype = getbufvar(a:srcname, '&filetype') let s:async_dictionary_list[source.path] = [{ \ 'filename' : source.path, \ 'cachename' : source.cache_name, \ }] endfunction"}}} function! s:make_cache_buffer(srcname) abort "{{{ if !s:should_create_cache(a:srcname) return endif call neocomplete#print_debug('make_cache_buffer: ' . a:srcname) if !s:exists_current_source() call s:initialize_source(a:srcname) if a:srcname ==# bufnr('%') " Force sync cache call s:make_cache_current_buffer(1, 1000) return endif endif let source = s:buffer_sources[a:srcname] let temp = neocomplete#cache#getfilename( \ 'buffer_temp', getpid() . '_' . a:srcname) let lines = getbufline(a:srcname, 1, '$') call writefile(lines, temp) " Create temporary file let source.cache_name = \ neocomplete#cache#async_load_from_file( \ 'buffer_cache', temp, \ source.keyword_pattern, 'B') let source.cached_time = localtime() let source.filetype = getbufvar(a:srcname, '&filetype') if source.filetype == '' let source.filetype = 'nothing' endif let s:async_dictionary_list[source.path] = [{ \ 'filename' : temp, \ 'cachename' : source.cache_name, \ }] endfunction"}}} function! s:check_changed_buffer(bufnr) abort "{{{ let source = s:buffer_sources[a:bufnr] let ft = getbufvar(a:bufnr, '&filetype') if ft == '' let ft = 'nothing' endif let filename = fnamemodify(bufname(a:bufnr), ':t') if filename == '' let filename = '[No Name]' endif return source.name != filename || source.filetype != ft endfunction"}}} function! s:check_source() abort "{{{ " Check new buffer. call map(filter(range(1, bufnr('$')), " \ (v:val != bufnr('%') || neocomplete#has_vimproc()) \ && (!has_key(s:buffer_sources, v:val) && buflisted(v:val) \ || (has_key(s:buffer_sources, v:val) && \ s:buffer_sources[v:val].cached_time \ < getftime(s:buffer_sources[v:val].path))) \ && (!neocomplete#is_locked(v:val) || \ g:neocomplete#disable_auto_complete) \ && s:should_create_cache(v:val) \ "), 's:make_cache_file(v:val)') " Remove unlisted buffers. call filter(s:buffer_sources, \ "v:key == bufnr('%') || buflisted(str2nr(v:key))") endfunction"}}} function! s:exists_current_source() abort "{{{ return has_key(s:buffer_sources, bufnr('%')) && \ !s:check_changed_buffer(bufnr('%')) endfunction"}}} function! s:make_cache_current_buffer(start, end) abort "{{{ let srcname = bufnr('%') " Make cache from current buffer. if !s:should_create_cache(srcname) return endif if !s:exists_current_source() call s:initialize_source(srcname) endif let source = s:buffer_sources[srcname] let keyword_pattern = source.keyword_pattern if keyword_pattern == '' return endif let words = [] lua << EOF do local words = vim.eval('words') local dup = {} local min_length = vim.eval('g:neocomplete#min_keyword_length') for linenr = vim.eval('a:start'), vim.eval('a:end') do local match = 0 while 1 do local match_str = vim.eval('matchstr(getline('..linenr.. '), keyword_pattern, ' .. match .. ')') if match_str == '' then break end if dup[match_str] == nil and string.len(match_str) >= min_length then dup[match_str] = 1 words:add(match_str) end -- Next match. match = vim.eval('matchend(getline(' .. linenr .. '), keyword_pattern, ' .. match .. ')') end end end EOF let source.words = neocomplete#util#uniq(source.words + words) endfunction"}}} function! s:check_async_cache(context) abort "{{{ for source in s:get_sources_list(a:context) if !has_key(s:async_dictionary_list, source.path) continue endif " Load from cache. let [loaded, file_cache] = neocomplete#cache#get_cache_list( \ 'buffer_cache', s:async_dictionary_list[source.path]) if loaded let source.words = file_cache endif if empty(s:async_dictionary_list[source.path]) call remove(s:async_dictionary_list, source.path) endif endfor endfunction"}}} function! s:clean() abort "{{{ " Remove temporary files for file in glob(printf('%s/%d_*', \ neocomplete#get_data_directory() . '/buffer_temp', \ getpid()), 1, 1) call delete(file) let cachefile = neocomplete#get_data_directory() . '/buffer_cache/' \ . substitute(substitute(file, ':', '=-', 'g'), '[/\\]', '=+', 'g') if filereadable(cachefile) call delete(cachefile) endif endfor endfunction"}}} " Command functions. "{{{ function! neocomplete#sources#buffer#make_cache(name) abort "{{{ if !neocomplete#is_enabled() call neocomplete#initialize() endif if a:name == '' let number = bufnr('%') else let number = bufnr(a:name) if number < 0 let bufnr = bufnr('%') " No swap warning. let save_shm = &shortmess set shortmess+=A " Open new buffer. execute 'silent! edit' fnameescape(a:name) let &shortmess = save_shm if bufnr('%') != bufnr setlocal nobuflisted execute 'buffer' bufnr endif endif let number = bufnr(a:name) endif call s:make_cache_file(number) endfunction"}}} "}}} let &cpo = s:save_cpo unlet s:save_cpo " vim: foldmethod=marker