1 " This file is part of NIT ( http://www.nitlanguage.org ).
3 " Licensed under the Apache License, Version 2.0 (the "License");
4 " you may not use this file except in compliance with the License.
5 " You may obtain a copy of the License at
7 " http://www.apache.org/licenses/LICENSE-2.0
9 " Unless required by applicable law or agreed to in writing, software
10 " distributed under the License is distributed on an "AS IS" BASIS,
11 " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 " See the License for the specific language governing permissions and
13 " limitations under the License.
15 " Nit plugin for Vim, provides some advanced features
21 if exists("loaded_nit_plugin")
24 let loaded_nit_plugin = 1
26 " Scan all relevant Nit modules for the current directory to autocomplete
28 " The guard `g:nit_complete_done` ensures that its body is executed only
29 " once. The call to `nitls -M` analyzses the current directory. However,
30 " updating the module list can be forced using ForceNitComplete.
32 " To activate, add the following line to ~/.vimrc
34 " autocmd Filetype nit call NitComplete()
35 function NitComplete()
36 if !exists("g:nit_complete_done")
37 let g:nit_complete_done = 1
39 " Reset or backup the original complete
40 if !exists("g:nit_complete_backup")
41 let g:nit_complete_backup = &complete
43 silent let &complete = g:nit_complete_backup
47 " This gives us better results for Nit
49 set completeopt=longest,menuone,preview
51 " Do not predict small 3 letters keywords (or their prefix), they slow down
52 " prediction and some also require double-enter on end of line.
53 let g:acp_behaviorKeywordIgnores = ['new', 'var', 'in', 'do', 'els', 'end', 'ret', 'for', 'fun']
55 " Use nitls to compute all interesting files from the current directory and the standard library
56 for file in split(system('nitls -M standard .', '\n'))
57 silent let &complete = &complete . ',s' . file
61 " Compatibility with AutoComplPop
62 let g:acp_completeOption = &complete
63 let g:acp_ignorecaseOption = &ignorecase
65 " Redraw in case the user pressed some keys while waiting
70 " Force updating the Nit modules used for autocomplete
72 " It is recommended to manually call this function as needed. It can be mapped
75 " map <F2> :call ForceNitComplete()
77 " For small projects (or fast computers) you might want to call it on each
79 function ForceNitComplete()
80 unlet! g:nit_complete_done
84 " Get path to the best metadata file named `name`
86 " Returns an empty string if not found.
87 fun NitMetadataFile(name)
88 " Where are the generated metadata files?
89 if empty($NIT_VIM_DIR)
90 let metadata_dir = $HOME . '/.vim/nit'
92 let metadata_dir = $NIT_VIM_DIR
95 let path = metadata_dir . '/' . a:name
97 " Is there generated custom metadata files?
98 if ! filereadable(path)
99 let path = s:script_dir . '/' . a:name
101 " Is there standard metadata files?
102 if ! filereadable(path)
110 " Internal function to search for lines in `path` corresponding to the partial
111 " word `base`. Adds found and formated match to `matches`.
113 " Will order the results in 3 levels:
115 " 2. Common prefix matches
116 " 3. Substring matches
117 fun NitOmnifuncAddFromFile(base, matches, path)
118 let prefix_matches = []
119 let substring_matches = []
121 let path = NitMetadataFile(a:path)
126 for line in readfile(path)
127 let words = split(line, '#====#', 1)
128 let name = get(words, 0, '')
133 call NitOmnifuncAddAMatch(a:matches, words, name)
134 elseif name =~ '^'.a:base
135 " Common-prefix match
136 call NitOmnifuncAddAMatch(prefix_matches, words, name)
137 elseif name =~ a:base
139 call NitOmnifuncAddAMatch(substring_matches, words, name)
143 " Assemble the final match list
144 call extend(a:matches, sort(prefix_matches))
145 call extend(a:matches, sort(substring_matches))
148 " Internal function to search parse the information from a metadata line
149 fun NitOmnifuncAddAMatch(matches, words, name)
150 let pretty = get(a:words, 1, '')
151 let synopsis = get(a:words, 2, '')
152 let desc = get(a:words, 3, '')
153 let desc = join(split(desc, '#nnnn#', 1), "\n")
154 if strlen(pretty) > 40
155 let pretty = pretty[:39] . '…'
157 call add(a:matches, {'word': a:name, 'abbr': pretty, 'menu': synopsis, 'info': desc, 'dup': 1})
160 " Omnifunc using metadata files generated by nitpick to offer better
161 " contextual autocomplete for Nit source code.
162 fun NitOmnifunc(findstart, base)
164 " locate the start of the word
165 let line = getline('.')
166 let start = col('.') - 1
167 while start > 0 && line[start - 1] =~ '\w'
172 " find keyword matching with "a:base"
175 " advanced suggestions
176 let cursor_line = getline('.')
178 " content of the line before the partial word
179 let line_prev_cursor = cursor_line[:col('.')-1]
181 let prev_char_at = strlen(line_prev_cursor) - 1
182 while prev_char_at > 0 && line_prev_cursor[prev_char_at] =~ '\s'
183 let prev_char_at -= 1
186 " Non whitespace char just before the partial word
187 let prev_char = line_prev_cursor[prev_char_at]
189 " Nity words on the current line before the partial word
190 let prev_words = split(line_prev_cursor, '\W\+')
192 " The word right before the partial word
193 let prev_word = get(prev_words, -1, '')
195 " Have we found a promising heuristic yet?
199 if prev_word == 'import' && ! (line_prev_cursor =~ 'fun')
201 call NitOmnifuncAddFromFile(a:base, matches, 'modules.txt')
204 " Classes (instanciable only)
205 if prev_word == 'new'
207 call NitOmnifuncAddFromFile(a:base, matches, 'constructors.txt')
210 " Other class references
211 if count(['class', 'super'], prev_word) > 0
213 call NitOmnifuncAddFromFile(a:base, matches, 'classes.txt')
217 if count(['class', 'super', 'nullable', 'isa', 'as'], prev_word) > 0 ||
218 \ line_prev_cursor =~ '[' || prev_char == ':' ||
219 \ (line_prev_cursor =~ 'fun' && line_prev_cursor =~ 'import' && (prev_word == 'import' || prev_char == ','))
221 call NitOmnifuncAddFromFile(a:base, matches, 'types.txt')
225 if prev_char == '.' || line_prev_cursor =~ '['
227 call NitOmnifuncAddFromFile(a:base, matches, 'properties.txt')
230 " If is nothing else...
232 " It may be a keyword
233 if strlen(a:base) > 0
234 for keyword in ['module', 'import', 'class', 'abstract', 'interface',
235 \'universal', 'enum', 'end', 'fun', 'type', 'init', 'redef', 'is',
236 \'do', 'var', 'extern', 'public', 'protected', 'private', 'intrude',
237 \'if', 'then', 'else', 'while', 'loop', 'for', 'in', 'and', 'or',
238 \'not', 'implies', 'return', 'continue', 'break', 'abort', 'assert',
239 \'new', 'isa', 'once', 'super', 'self', 'true', 'false', 'null',
240 \'as', 'nullable', 'isset', 'label']
242 if keyword =~ '^' . a:base
243 call add(matches, keyword)
248 " it may still be a method call or property access
249 call NitOmnifuncAddFromFile(a:base, matches, 'properties.txt')
252 return {'words': matches, 'refresh': 'always'}
256 " Activate the omnifunc on Nit files
257 autocmd FileType nit set omnifunc=NitOmnifunc
259 let s:script_dir = fnamemodify(resolve(expand('<sfile>:p')), ':h')