1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
" Entry point for plugin
function! spellfile_local#() abort
" If this is a special buffer, don't do anything
if index(['nofile', 'quickfix', 'help'], &buftype) >= 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]
let encoding = &encoding
" Make a list of all the spellfile names for which we want to search in
" every directory; the first is the normal lang.encoding.add, the second is
" filename.lang.encoding.add, and the third, if there's a filetype set, is
" filetype.lang.encoding.add.
"
let basenames = [s:Filename([lang, encoding, 'add'])]
let path = expand('<afile>:p')
call add(
\ basenames,
\ s:Filename(['path', path, lang, encoding, 'add'])
\)
let filetype = &filetype
if filetype !=# ''
call add(
\ basenames,
\ s:Filename(['filetype', filetype, lang, encoding, 'add'])
\)
endif
" Now make a list of all of the directories in which those files will be
" searched, and where applicable, created; the method for doing this depends
" on whether we have a configured list of directories or not
"
let dirnames = []
" If we have a list of directories to use as the base for 'spellfile' /spell
" subdirectories, we'll add all of them to the list with /spell suffixed,
" regardless of whether Vim can write to them.
"
if exists('g:spellfile_local_dirs') && !empty(g:spellfile_local_dirs)
for path in g:spellfile_local_dirs
call add(dirnames, path.'/spell')
endfor
" Failing that, do what Vim does by default: use the first *writeable* entry
" in 'runtimepath'. If none of them are writable, we raise an exception.
"
else
for path in s:OptionSplit(&runtimepath)
if filewritable(path) != 2
continue
endif
call add(dirnames, path.'/spell')
break
endfor
if empty(dirnames)
echoerr 'No writable runtime dirs for ''spellfile'''
return
endif
endif
" Attempt to create the first directory in the list if it doesn't exist
" already. Just let any error happen and reach the user.
"
let dirname = expand(dirnames[0])
if !isdirectory(dirname)
call mkdir(dirname, 'p', 0700)
endif
" Now we'll actually combine those two together to make a long list of
" spellfiles, and then set the option
"
let spellfiles = []
for dirname in dirnames
for basename in basenames
call add(spellfiles, join([dirname, basename], '/'))
endfor
endfor
let &l:spellfile = s:OptionJoin(spellfiles)
endfunction
" Escape a path for use as a valid option file name; replace any characters
" not in 'isfname' with percent symbols
function! s:Filename(parts) abort
let filename = ''
for char in split(join(a:parts, '.'), '\zs')
let filename .= (char !=# '_' && char !=# '/' && char =~# '^\f$')
\ ? char
\ : '%'
endfor
return filename
endfunction
" Join a list of strings into a comma-separated option
function! s:OptionJoin(list) abort
return join(map(
\ copy(a:list),
\ 'substitute(v:val, ''\\\@<!,'', ''\\,'', ''g'')',
\), ',')
endfunction
" Split a comma-separated option into a list of strings
function! s:OptionSplit(string) abort
return map(
\ split(a:string, '\\\@<!,[, ]*'),
\ 'substitute(v:val, ''\\,'', '''', ''g'')',
\)
endfunction
|