<commit_before>" This file is part of NIT ( http://www.nitlanguage.org ).
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
"		 http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.

" Nit plugin for Vim, provides some advanced features

if v:version < 700
	finish
endif

if exists("loaded_nit_plugin")
	finish
endif
let loaded_nit_plugin = 1

" Scan all relevant Nit modules for the current directory to autocomplete
"
" The guard `g:nit_complete_done` ensures that its body is executed only
" once. The call to `nitls -M` analyzses the current directory. However,
" updating the module list can be forced using ForceNitComplete.
"
" To activate, add the following line to ~/.vimrc
"
" autocmd Filetype nit call NitComplete()
function NitComplete()
	if !exists("g:nit_complete_done")
		let g:nit_complete_done = 1

		" Reset or backup the original complete
		if !exists("g:nit_complete_backup")
			let g:nit_complete_backup = &complete
		else
			silent let &complete = g:nit_complete_backup
			silent set complete?
		endif

		" This gives us better results for Nit
		set noignorecase
		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.
		let g:acp_behaviorKeywordIgnores = ['new', 'var', 'in', 'do', 'els', 'end', 'ret', 'for', 'fun']

		" Use nitls to compute all interesting files from the current directory and the standard library
		for file in split(system('nitls -M standard .', '\n'))
			silent let &complete = &complete . ',s' . file
			silent set complete?
		endfor

		" Compatibility with AutoComplPop
		let g:acp_completeOption = &complete
		let g:acp_ignorecaseOption = &ignorecase

		" Redraw in case the user pressed some keys while waiting
		redraw!
	endif
endfunction

" Force updating the Nit modules used for autocomplete
"
" It is recommended to manually call this function as needed. It can be mapped
" to a key with:
"
" map <F2> :call ForceNitComplete()
"
" For small projects (or fast computers) you might want to call it on each
" file save or load.
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[:39] . '…'
	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<commit_msg>misc/vim: extract the service to find metadata files from the omnifunc<commit_after>" This file is part of NIT ( http://www.nitlanguage.org ).
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
"		 http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.

" Nit plugin for Vim, provides some advanced features

if v:version < 700
	finish
endif

if exists("loaded_nit_plugin")
	finish
endif
let loaded_nit_plugin = 1

" Scan all relevant Nit modules for the current directory to autocomplete
"
" The guard `g:nit_complete_done` ensures that its body is executed only
" once. The call to `nitls -M` analyzses the current directory. However,
" updating the module list can be forced using ForceNitComplete.
"
" To activate, add the following line to ~/.vimrc
"
" autocmd Filetype nit call NitComplete()
function NitComplete()
	if !exists("g:nit_complete_done")
		let g:nit_complete_done = 1

		" Reset or backup the original complete
		if !exists("g:nit_complete_backup")
			let g:nit_complete_backup = &complete
		else
			silent let &complete = g:nit_complete_backup
			silent set complete?
		endif

		" This gives us better results for Nit
		set noignorecase
		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.
		let g:acp_behaviorKeywordIgnores = ['new', 'var', 'in', 'do', 'els', 'end', 'ret', 'for', 'fun']

		" Use nitls to compute all interesting files from the current directory and the standard library
		for file in split(system('nitls -M standard .', '\n'))
			silent let &complete = &complete . ',s' . file
			silent set complete?
		endfor

		" Compatibility with AutoComplPop
		let g:acp_completeOption = &complete
		let g:acp_ignorecaseOption = &ignorecase

		" Redraw in case the user pressed some keys while waiting
		redraw!
	endif
endfunction

" Force updating the Nit modules used for autocomplete
"
" It is recommended to manually call this function as needed. It can be mapped
" to a key with:
"
" map <F2> :call ForceNitComplete()
"
" For small projects (or fast computers) you might want to call it on each
" file save or load.
function ForceNitComplete()
	unlet! g:nit_complete_done
	call NitComplete()
endfunction

" Get path to the best metadata file named `name`
"
" Returns an empty string if not found.
fun NitMetadataFile(name)
	" 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:name

	" Is there generated custom metadata files?
	if ! filereadable(path)
		let path = s:script_dir . '/' . a:name

		" Is there standard metadata files?
		if ! filereadable(path)
			return ''
		endif
	endif

	return path
endfun

" 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 = []

	let path = NitMetadataFile(a:path)
	if empty(path)
		return
	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[:39] . '…'
	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
{'fraction_non_alphanumeric': 0.09571614019904803, 'fraction_numerical': 0.00640415404586759, 'mean_word_length': 4.057330415754923, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4975740', 'n_tokens_mistral': 3938, 'n_tokens_neox': 3621, 'n_words': 1399}