From 4307e671f3c23ea34b51230f008cd46992e15ea8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Wed, 4 Feb 2015 12:01:57 -0500 Subject: [PATCH] misc/vim: use the metadata files for a better autocompletion MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- misc/vim/plugin/nit.vim | 154 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/misc/vim/plugin/nit.vim b/misc/vim/plugin/nit.vim index ea308a5..d58df14 100644 --- a/misc/vim/plugin/nit.vim +++ b/misc/vim/plugin/nit.vim @@ -46,7 +46,7 @@ function NitComplete() " This gives us better results for Nit set noignorecase - set completeopt=longest,menuone + set completeopt=longest,menuone,preview " Do not predict small 3 letters keywords (or their prefix), they slow down " prediction and some also require double-enter on end of line. @@ -80,3 +80,155 @@ function ForceNitComplete() unlet! g:nit_complete_done call NitComplete() endfunction + +" Internal function to search for lines in `path` corresponding to the partial +" word `base`. Adds found and formated match to `matches`. +" +" Will order the results in 3 levels: +" 1. Exact matches +" 2. Common prefix matches +" 3. Substring matches +fun NitOmnifuncAddFromFile(base, matches, path) + let prefix_matches = [] + let substring_matches = [] + + " Where are the generated metadata files? + if empty($NIT_VIM_DIR) + let metadata_dir = $HOME . '/.vim/nit' + else + let metadata_dir = $NIT_VIM_DIR + end + + let path = metadata_dir . '/' . a:path + " Is there generated custom metadata files? + if ! filereadable(path) + let path = s:script_dir . '/' . a:path + + " Is there standard metadata files? + if ! filereadable(path) + return + endif + endif + + for line in readfile(path) + let words = split(line, '#====#', 1) + let name = get(words, 0, '') + + " Add? + if name == a:base + " Exact match + call NitOmnifuncAddAMatch(a:matches, words, name) + elseif name =~ '^'.a:base + " Common-prefix match + call NitOmnifuncAddAMatch(prefix_matches, words, name) + elseif name =~ a:base + " Substring match + call NitOmnifuncAddAMatch(substring_matches, words, name) + endif + endfor + + " Assemble the final match list + call extend(a:matches, sort(prefix_matches)) + call extend(a:matches, sort(substring_matches)) +endfun + +" Internal function to search parse the information from a metadata line +fun NitOmnifuncAddAMatch(matches, words, name) + let pretty = get(a:words, 1, '') + let synopsis = get(a:words, 2, '') + let desc = get(a:words, 3, '') + let desc = join(split(desc, '#nnnn#', 1), "\n") + if strlen(pretty) > 40 + let pretty = pretty[:37] . '...' + endif + call add(a:matches, {'word': a:name, 'abbr': pretty, 'menu': synopsis, 'info': desc, 'dup': 1}) +endfun + +" Omnifunc using metadata files generated by nitpick to offer better +" contextual autocomplete for Nit source code. +fun NitOmnifunc(findstart, base) + if a:findstart + " locate the start of the word + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\w' + let start -= 1 + endwhile + return start + else + " find keyword matching with "a:base" + let matches = [] + + " Advanced suggestions + let cursor_line = getline('.') + + " Content of the line before the partial word + let line_prev_cursor = cursor_line[:col('.')-1] + + let prev_char_at = strlen(line_prev_cursor) - 1 + while prev_char_at > 0 && line_prev_cursor[prev_char_at] =~ '\s' + let prev_char_at -= 1 + endwhile + + " Non whitespace char just before the partial word + let prev_char = line_prev_cursor[prev_char_at] + + " Nity words on the current line before the partial word + let prev_words = split(line_prev_cursor, '\W\+') + + " The word right before the partial word + let prev_word = get(prev_words, -1, '') + + " Have we found a promising heuristic yet? + let handled = 0 + + " Modules + if prev_word == 'import' && ! (line_prev_cursor =~ 'fun') + let handled = 1 + call NitOmnifuncAddFromFile(a:base, matches, 'modules.txt') + endif + + " Classes + if count(['new', 'super', 'class', 'nullable'], prev_word) > 0 || + \ line_prev_cursor =~ '[' || prev_char == ':' || + \ (line_prev_cursor =~ 'fun' && line_prev_cursor =~ 'import' && (prev_word == 'import' || prev_char == ',')) + let handled = 1 + call NitOmnifuncAddFromFile(a:base, matches, 'classes.txt') + endif + + " Properties + if prev_char == '.' || line_prev_cursor =~ '[' + let handled = 1 + call NitOmnifuncAddFromFile(a:base, matches, 'properties.txt') + endif + + " If is nothing else... + if !handled + " It may be a keyword + if strlen(a:base) > 0 + for keyword in ['module', 'import', 'class', 'abstract', 'interface', + \'universal', 'enum', 'end', 'fun', 'type', 'init', 'redef', 'is', + \'do', 'var', 'extern', 'public', 'protected', 'private', 'intrude', + \'if', 'then', 'else', 'while', 'loop', 'for', 'in', 'and', 'or', + \'not', 'implies', 'return', 'continue', 'break', 'abort', 'assert', + \'new', 'isa', 'once', 'super', 'self', 'true', 'false', 'null', + \'as', 'nullable', 'isset', 'label'] + + if keyword =~ '^' . a:base + call add(matches, keyword) + endif + endfor + endif + + " it may still be a method call or property access + call NitOmnifuncAddFromFile(a:base, matches, 'properties.txt') + endif + + return {'words': matches, 'refresh': 'always'} + endif +endfun + +" Activate the omnifunc on Nit files +autocmd FileType nit set omnifunc=NitOmnifunc + +let s:script_dir = fnamemodify(resolve(expand(':p')), ':h') -- 1.7.9.5