aboutsummaryrefslogtreecommitdiff
path: root/autoload/spellfile_local.vim
blob: 1ae937bdac8ef1fed392f04ef07b10bfa1d3f0e3 (plain) (blame)
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