From ba42c743f57ffbc0546c8c11fe92b3f95a15e6cc Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Thu, 19 Dec 2019 17:19:41 +1300 Subject: Overhaul spellfile_local.vim plugin Ready to be spun out into its own distribution shortly. --- vim/autoload/spellfile_local.vim | 186 ++++++++++++++++++++++++++++----------- vim/plugin/spellfile_local.vim | 18 ++-- 2 files changed, 147 insertions(+), 57 deletions(-) (limited to 'vim') diff --git a/vim/autoload/spellfile_local.vim b/vim/autoload/spellfile_local.vim index e119b718..7dbe8fb5 100644 --- a/vim/autoload/spellfile_local.vim +++ b/vim/autoload/spellfile_local.vim @@ -1,72 +1,154 @@ -function! s:SplitOption(string) abort - return map( - \ split(a:string, '\\\@= 0 + return + endif + + " Get the first item in the spelling languages list, bail if there aren't + " any; strip any regional suffix (e.g. en_NZ), too, as the final 'spellfile' + " value won't tolerate it + " + let spelllangs = s:OptionSplit(&spelllang) + if len(spelllangs) == 0 + return + endif + let lang = split(spelllangs[0], '_')[0] + + " Use current encoding + let encoding = &encoding + + " Start a list of spellfiles + let spellfiles = [] + + " Imitate Vim's behaviour in creating a `spell` subdir in the first + " writeable element of 'runtimepath' + " + for runtimepath in s:OptionSplit(&runtimepath) + let path = s:Path(join(add( + \ split(runtimepath, '/', 1) + \,'spell' + \), '/'), lang, encoding) + if path !=# '' + call add(spellfiles, path) + break + endif + endfor + + " Still no 'spellfile'? Quietly give up + if len(spellfiles) == 0 + return + endif + + " Get the path to the spelling files directory + let dir = fnamemodify(spellfiles[0], ':h') + + " Specify the name and type of spelling files we'll add, with a list of + " two-key dictionaries. For each of these, the `name` is used as the + " subdirectory, and the `value` as the first component of the filename. We + " + let types = [ + \ { 'name': 'path', 'value': expand('%:p') } + \,{ 'name': 'filetype', 'value': &filetype } + \] + + " Iterate through the specified types to add them to the spelling files list + for type in types + + " Add a new calculated path to the spellfiles list, if valid + let spellfile = s:Path(dir, lang, encoding, type) + if spellfile !=# '' + call add(spellfiles, spellfile) + endif + + endfor + + " Set the spellfile path list to the concatenated list + let &spellfile = s:OptionJoin(spellfiles) + endfunction -function! s:JoinOption(list) abort - return join(map( - \ a:list - \,'substitute(v:val, ''\\\@ 0 + let type = a:1 endif - let spellfiles = s:SplitOption(&spellfile) - if len(spellfiles) != 1 || spellfiles[0] ==# '' - return + " Make lists representing the directory path elements and the + " period-separated filename + " + let dir = split(a:dir, '/', 1) + let file = [a:lang, a:encoding, 'add'] + + " If we have an optional type, extend the directory with another element + " according to its name, and insert the value before the filename, + " e.g. append "filetype" to the directory, and insert the current value of + " &filetype before the filename; if we have a type but a blank value, which + " is not necessarily an error condition, stop here and return nothing + " + if exists('type') + if type['value'] ==# '' + return + endif + call add(dir, type['name']) + call insert(file, type['value']) endif - let spelldir = fnamemodify(spellfiles[0], ':h') - if spelldir ==# '' - echoerr 'Blank directory' + " Ensure the directory is in place, trying to create it if need be, and that + " all of it passes an 'isfname' filter, since 'spellfile' checks that + " + let ds = join(dir, '/') + if ds !~# '^\f\+$' + \ || filewritable(ds) != 2 && !mkdir(ds, '0700', 'p') + return endif - try - let path = tr(expand('%:p'), '/', '%') - if path ==# '' - echoerr 'Blank path' - endif - call s:Establish(spelldir.'/path') - call add(spellfiles, spelldir.'/path/'.join([ - \ path - \,spelllang - \,&encoding - \,'add' - \], '.')) - - if &filetype ==# '' - echoerr 'Blank filetype' - endif - call s:Establish(spelldir.'/filetype') - call add(spellfiles, spelldir.'/filetype/'.join([ - \ &filetype - \,spelllang - \,&encoding - \,'add' - \], '.')) - catch - endtry - - let &l:spellfile = s:JoinOption(spellfiles) + " Build the full spellfile path, escaping the filename appropriately, and + " return it as a path string + " + let path = add(copy(dir), s:Escape(join(file, '.'))) + return join(path, '/') endfunction diff --git a/vim/plugin/spellfile_local.vim b/vim/plugin/spellfile_local.vim index f6918bfb..07307754 100644 --- a/vim/plugin/spellfile_local.vim +++ b/vim/plugin/spellfile_local.vim @@ -1,12 +1,20 @@ +" +" spellfile_local.vim: Set extra 'spellfile' elements for full file paths and +" filetype, to give the option of adding to file-specific or filetype-specific +" spelling word lists. +" +" Author: Tom Ryder +" License: Same as Vim itself +" if exists('loaded_spellfile_local') || &compatible finish endif let loaded_spellfile_local = 1 -command! -bar SetLocalSpellFiles - \ call spellfile_local#() - +" For various events involving establishing or renaming a file buffer or +" changing its filetype, rebuild the 'spellfile' definition accordingly +" augroup spellfile_local - autocmd BufNew,BufRead * - \ SetLocalSpellFiles + autocmd BufFilePost,BufNewFile,BufRead,EncodingChanged,FileType * + \ call spellfile_local#() augroup END -- cgit v1.2.3