1" Vim script to download a missing spell file
2" Maintainer:	Bram Moolenaar <[email protected]>
3" Last Change:	2020 Jul 10
4
5if !exists('g:spellfile_URL')
6  " Always use https:// because it's secure.  The certificate is for nluug.nl,
7  " thus we can't use the alias ftp.vim.org here.
8  let g:spellfile_URL = 'https://ftp.nluug.nl/pub/vim/runtime/spell'
9endif
10let s:spellfile_URL = ''    " Start with nothing so that s:donedict is reset.
11
12" This function is used for the spellfile plugin.
13function! spellfile#LoadFile(lang)
14  " If the netrw plugin isn't loaded we silently skip everything.
15  if !exists(":Nread")
16    if &verbose
17      echomsg 'spellfile#LoadFile(): Nread command is not available.'
18    endif
19    return
20  endif
21  let lang = tolower(a:lang)
22
23  " If the URL changes we try all files again.
24  if s:spellfile_URL != g:spellfile_URL
25    let s:donedict = {}
26    let s:spellfile_URL = g:spellfile_URL
27  endif
28
29  " I will say this only once!
30  if has_key(s:donedict, lang . &enc)
31    if &verbose
32      echomsg 'spellfile#LoadFile(): Tried this language/encoding before.'
33    endif
34    return
35  endif
36  let s:donedict[lang . &enc] = 1
37
38  " Find spell directories we can write in.
39  let [dirlist, dirchoices] = spellfile#GetDirChoices()
40  if len(dirlist) == 0
41    let dir_to_create = spellfile#WritableSpellDir()
42    if &verbose || dir_to_create != ''
43      echomsg 'spellfile#LoadFile(): There is no writable spell directory.'
44    endif
45    if dir_to_create != ''
46      if confirm("Shall I create " . dir_to_create, "&Yes\n&No", 2) == 1
47	" After creating the directory it should show up in the list.
48	call mkdir(dir_to_create, "p")
49	let [dirlist, dirchoices] = spellfile#GetDirChoices()
50      endif
51    endif
52    if len(dirlist) == 0
53      return
54    endif
55  endif
56
57  let msg = 'Cannot find spell file for "' . lang . '" in ' . &enc
58  let msg .= "\nDo you want me to try downloading it?"
59  if confirm(msg, "&Yes\n&No", 2) == 1
60    let enc = &encoding
61    if enc == 'iso-8859-15'
62      let enc = 'latin1'
63    endif
64    let fname = lang . '.' . enc . '.spl'
65
66    " Split the window, read the file into a new buffer.
67    " Remember the buffer number, we check it below.
68    new
69    let newbufnr = winbufnr(0)
70    setlocal bin fenc=
71    echo 'Downloading ' . fname . '...'
72    call spellfile#Nread(fname)
73    if getline(2) !~ 'VIMspell'
74      " Didn't work, perhaps there is an ASCII one.
75      " Careful: Nread() may have opened a new window for the error message,
76      " we need to go back to our own buffer and window.
77      if newbufnr != winbufnr(0)
78	let winnr = bufwinnr(newbufnr)
79	if winnr == -1
80	  " Our buffer has vanished!?  Open a new window.
81	  echomsg "download buffer disappeared, opening a new one"
82	  new
83	  setlocal bin fenc=
84	else
85	  exe winnr . "wincmd w"
86	endif
87      endif
88      if newbufnr == winbufnr(0)
89	" We are back the old buffer, remove any (half-finished) download.
90        g/^/d
91      else
92	let newbufnr = winbufnr(0)
93      endif
94
95      let fname = lang . '.ascii.spl'
96      echo 'Could not find it, trying ' . fname . '...'
97      call spellfile#Nread(fname)
98      if getline(2) !~ 'VIMspell'
99	echo 'Sorry, downloading failed'
100	exe newbufnr . "bwipe!"
101	return
102      endif
103    endif
104
105    " Delete the empty first line and mark the file unmodified.
106    1d
107    set nomod
108
109    let msg = "In which directory do you want to write the file:"
110    for i in range(len(dirlist))
111      let msg .= "\n" . (i + 1) . '. ' . dirlist[i]
112    endfor
113    let dirchoice = confirm(msg, dirchoices) - 2
114    if dirchoice >= 0
115      if exists('*fnameescape')
116	let dirname = fnameescape(dirlist[dirchoice])
117      else
118	let dirname = escape(dirlist[dirchoice], ' ')
119      endif
120      setlocal fenc=
121      exe "write " . dirname . '/' . fname
122
123      " Also download the .sug file, if the user wants to.
124      let msg = "Do you want me to try getting the .sug file?\n"
125      let msg .= "This will improve making suggestions for spelling mistakes,\n"
126      let msg .= "but it uses quite a bit of memory."
127      if confirm(msg, "&No\n&Yes") == 2
128	g/^/d
129	let fname = substitute(fname, '\.spl$', '.sug', '')
130	echo 'Downloading ' . fname . '...'
131	call spellfile#Nread(fname)
132	if getline(2) =~ 'VIMsug'
133	  1d
134	  exe "write " . dirname . '/' . fname
135	  set nomod
136	else
137	  echo 'Sorry, downloading failed'
138	  " Go back to our own buffer/window, Nread() may have taken us to
139	  " another window.
140	  if newbufnr != winbufnr(0)
141	    let winnr = bufwinnr(newbufnr)
142	    if winnr != -1
143	      exe winnr . "wincmd w"
144	    endif
145	  endif
146	  if newbufnr == winbufnr(0)
147	    set nomod
148	  endif
149	endif
150      endif
151    endif
152
153    " Wipe out the buffer we used.
154    exe newbufnr . "bwipe"
155  endif
156endfunc
157
158" Read "fname" from the server.
159function! spellfile#Nread(fname)
160  " We do our own error handling, don't want a window for it.
161  if exists("g:netrw_use_errorwindow")
162    let save_ew = g:netrw_use_errorwindow
163  endif
164  let g:netrw_use_errorwindow=0
165
166  if g:spellfile_URL =~ '^ftp://'
167    " for an ftp server use a default login and password to avoid a prompt
168    let machine = substitute(g:spellfile_URL, 'ftp://\([^/]*\).*', '\1', '')
169    let dir = substitute(g:spellfile_URL, 'ftp://[^/]*/\(.*\)', '\1', '')
170    exe 'Nread "' . machine . ' anonymous vim7user ' . dir . '/' . a:fname . '"'
171  else
172    exe 'Nread ' g:spellfile_URL . '/' . a:fname
173  endif
174
175  if exists("save_ew")
176    let g:netrw_use_errorwindow = save_ew
177  else
178    unlet g:netrw_use_errorwindow
179  endif
180endfunc
181
182" Get a list of writable spell directories and choices for confirm().
183function! spellfile#GetDirChoices()
184  let dirlist = []
185  let dirchoices = '&Cancel'
186  for dir in split(globpath(&rtp, 'spell'), "\n")
187    if filewritable(dir) == 2
188      call add(dirlist, dir)
189      let dirchoices .= "\n&" . len(dirlist)
190    endif
191  endfor
192  return [dirlist, dirchoices]
193endfunc
194
195function! spellfile#WritableSpellDir()
196  if has("unix")
197    " For Unix always use the $HOME/.vim directory
198    return $HOME . "/.vim/spell"
199  endif
200  for dir in split(&rtp, ',')
201    if filewritable(dir) == 2
202      return dir . "/spell"
203    endif
204  endfor
205  return ''
206endfunction
207