aboutsummaryrefslogtreecommitdiff
path: root/autoload/spellfile_local.vim
blob: dc9d2cca0d522002082a7afbb994bf486d9ee678 (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
" 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]

  " Make a list of all the spellfile names for which we want to search in
  " every directory; first is the normal lang.encoding.add, then if there's
  " a path set filename.lang.encoding.add, and then if there's a filetype set,
  " filetype.lang.encoding.add.
  "
  let basenames = [s:Filename([lang, &encoding, 'add'])]
  let path = expand('%:p')
  if path !=# ''
    call add(
          \ basenames,
          \ s:Filename(['path', path, lang, &encoding, 'add'])
          \)
  endif
  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
" that won't work nicely as filenames with percent signs
function! s:Filename(parts) abort
  let pat = has('win32') || has('win64')
        \ ? '\c[^[:fname:]]\|\\'
        \ : '\c[^[:fname:]]\|/'
  return substitute(join(a:parts, '.'), pat, '%', 'g')
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