]> gitweb.factorcode.org Git - factor.git/blob - misc/vim/autoload/factor.vim
misc/vim: Further improve `:FactorVocab` completion
[factor.git] / misc / vim / autoload / factor.vim
1 " Location:     autoload/factor.vim
2
3 " Section: Utilities
4
5 let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/')..'\+'
6
7 " Remove the separator at the end of a:path.
8 " (Modified from vim-jp/vital.vim.)
9 function! s:remove_last_path_sep(path) abort
10   return substitute(a:path, s:path_sep_pattern..'$', '', '')
11 endfunction
12
13 function! s:ensure_last_path_sep(path) abort
14   return a:path =~# s:path_sep_pattern..'$' ? a:path
15         \ : a:path..(!exists('+shellslash') || &shellslash ? '/' : '\')
16 endfunction
17
18 if !exists('g:FactorGlobEscape')
19   if exists('+shellslash') && !&shellslash
20     let g:FactorGlobEscape = '*[]?`{$'
21   else
22     let g:FactorGlobEscape = '*[]?`{$\'
23   endif
24 endif
25
26 " (Based on tpope/vim-scriptease.)
27 function! s:glob(pattern, nosuf = 0, alllinks = 0) abort
28   if v:version >= 704
29     return glob(a:pattern, a:nosuf, 1, a:alllinks)
30   else
31     return split(glob(a:pattern, a:nosuf), "\n")
32   endif
33 endfunction
34
35 " Section: File discovery & globbing
36
37 function! factor#get_vocab_roots() abort
38   if exists('g:FactorVocabRoots')
39     return g:FactorVocabRoots
40   endif
41   if !exists('g:FactorAdditionalVocabRoots')
42     try
43       let g:FactorAdditionalVocabRoots = map(
44             \ filter(readfile(fnamemodify('~/.factor-roots', ':p')), 'v:val !=# '''''),
45             \ 's:remove_last_path_sep(v:val)')
46     catch /^Vim\%((\a\+)\)\=:E484/
47       let g:FactorAdditionalVocabRoots = []
48     endtry
49   endif
50   let g:FactorVocabRoots =
51         \ map(g:FactorDefaultVocabRoots + g:FactorAdditionalVocabRoots, 's:remove_last_path_sep(v:val)')
52   return g:FactorVocabRoots
53 endfunction
54
55 function! factor#expand_vocab_roots(vocab_roots)
56   let sep = !exists('+shellslash') || &shellslash ? '/' : '\'
57   let expanded_vocab_roots = []
58   for vocab_root in a:vocab_roots
59     if vocab_root =~# '^vocab:'
60       let expanded_vocab_roots_len = len(expanded_vocab_roots)
61       let i = 0
62       while i < expanded_vocab_roots_len
63         call add(expanded_vocab_roots,
64               \ s:ensure_last_path_sep(expanded_vocab_roots[i])..vocab_root[6:])
65         let i += 1
66       endwhile
67     else
68       call add(expanded_vocab_roots,
69             \ vocab_root =~# '^resource:' ? g:FactorResourcePath..vocab_root[9:] : vocab_root)
70     endif
71   endfor
72   return expanded_vocab_roots
73 endfunction
74
75 function! factor#detect_parent_vocab_roots(vocab_roots, fname, expr, nosuf = 0, alllinks = 0) abort
76   let sep = !exists('+shellslash') || &shellslash ? '/' : '\'
77   let parent_vocab_roots = []
78   let expanded_vocab_roots = {}
79   for expanded_vocab_root in factor#expand_vocab_roots(a:vocab_roots)
80     let expanded_vocab_roots[fnamemodify(expanded_vocab_root, ':p')] = 1
81   endfor
82   let current_path = fnamemodify(a:fname, ':p')
83   while current_path !=# ''
84     let current_path_glob = s:ensure_last_path_sep(escape(current_path, g:FactorGlobEscape))
85     let paths = s:glob(current_path_glob..a:expr, a:nosuf, a:alllinks)
86     for path in paths
87       let path = fnamemodify(path, ':p')
88       if get(expanded_vocab_roots, path, 0)
89         call add(parent_vocab_roots, path)
90       end
91     endfor
92     let current_path = current_path ==# '/' ? '' : fnamemodify(current_path, ':h')
93   endwhile
94   return parent_vocab_roots
95 endfunction
96
97 " (Based on tpope/vim-scriptease.)
98 function! factor#glob(expr, vocab = 0, trailing_dir_sep = 0, output = 0, nosuf = 0, alllinks = 0) abort
99   let sep = !exists('+shellslash') || &shellslash ? '/' : '\'
100   let expr = a:vocab ? 'vocab:'..substitute(a:expr, '\.', sep, 'g') : a:expr
101   let found = {}
102   if expr =~# '^resource:'
103     for path_root in s:glob(escape(g:FactorResourcePath, g:FactorGlobEscape), 1, 1)
104       let path_root = fnamemodify(path_root, ':p')
105       for path in s:glob(escape(path_root, g:FactorGlobEscape)..expr[9:], a:nosuf, a:alllinks)
106         if a:trailing_dir_sep == 1 | let path = fnamemodify(path, ':p') | elseif a:trailing_dir_sep == 2
107           let path = s:remove_last_path_sep(fnamemodify(path, ':p'))
108         endif
109         let path = a:output == 0 ? 'resource:'..path[strlen(path_root):] : path
110         let found[path] = 1
111       endfor
112     endfor
113   elseif expr =~# '^vocab:'
114     let expanded_vocab_roots = factor#expand_vocab_roots(factor#get_vocab_roots())
115     for vocab_root in expanded_vocab_roots
116       for path_root in s:glob(escape(vocab_root, g:FactorGlobEscape), 1, 1)
117         let path_root = fnamemodify(path_root, ':p')
118         for path in s:glob(escape(path_root, g:FactorGlobEscape)..expr[6:], a:nosuf, a:alllinks)
119           if a:trailing_dir_sep == 1 | let path = fnamemodify(path, ':p') | elseif a:trailing_dir_sep == 2
120             let path = s:remove_last_path_sep(fnamemodify(path, ':p'))
121           endif
122           let path = a:output == 0 ? 'vocab:'..path[strlen(path_root):] : a:output == 1 ? path
123                 \ : substitute(path[strlen(path_root):], s:path_sep_pattern, '.', 'g')
124           let found[path] = 1
125         endfor
126       endfor
127     endfor
128   else
129     if expr =~# '^\%[resource]\*' | let found['resource:'] = 1 | endif
130     if expr =~# '^\%[vocab]\*' | let found['vocab:'] = 1 | endif
131     for expr_path in s:glob(expr, 1, 1)
132       let expr_path = fnamemodify(expr_path, ':p')
133       for vocab_root in factor#get_vocab_roots()
134         if vocab_root =~# '^resource:|^vocab:' | continue | endif
135         for path_root in s:glob(escape(vocab_root, g:FactorGlobEscape), 1, 1)
136           let path_root = fnamemodify(path_root, ':p')
137           if expr_path[0:strlen(path_root)] !=# path_root | break | endif
138           for path in s:glob(escape(path_root, g:FactorGlobEscape)..expr[strlen(path_root):], a:nosuf, a:alllinks)
139             if a:trailing_dir_sep == 1 | let path = fnamemodify(path, ':p') | elseif a:trailing_dir_sep == 2
140               let path = s:remove_last_path_sep(fnamemodify(path, ':p'))
141             endif
142             let path = a:output == 0 ? path[strlen(path_root):] : a:output == 1 ? path
143                   \ : substitute(path[strlen(path_root):], s:path_sep_pattern, '.', 'g')
144             let found[path] = 1
145           endfor
146         endfor
147       endfor
148     endfor
149   endif
150   return sort(keys(found))
151 endfunction
152
153 " Section: Completion
154
155 function! factor#complete_glob(arg_lead, cmd_line, cursor_pos) abort
156   return factor#glob(a:arg_lead..'*', 0, 1)
157 endfunction
158
159 function! factor#complete_vocab_glob(arg_lead, cmd_line, cursor_pos) abort
160   return factor#glob(a:arg_lead..'*.', 1, 2, 2)
161 endfunction
162
163 " Section: Commands
164
165 function! factor#go_to_vocab_command(count, cmd, vocab) abort
166   let sep = !exists('+shellslash') || &shellslash ? '/' : '\'
167   let vocab_glob = 'vocab:'..substitute(a:vocab, '\.', sep, 'g')..sep..matchstr(a:vocab, '[^.]*$')..'.factor'
168   let vocab_file = get(factor#glob(vocab_glob, 0, 2, 1), a:count - 1, 0)
169   if !!vocab_file
170     return 'echoerr '..string('Factor: Can''t find vocabulary '..a:vocab..' in vocabulary roots')
171   endif
172   return a:cmd..' '..fnameescape(vocab_file)
173 endfunction
174
175 function! factor#make_vocab_command(count, cmd, vocab) abort
176   let sep = !exists('+shellslash') || &shellslash ? '/' : '\'
177   let new_vocab_root = FactorNewVocabRoot()
178   let vocab_dir = get(factor#glob(new_vocab_root, 0, 1, 1), a:count - 1, 0)
179   if !!vocab_dir
180       return 'echoerr '..string('Factor: Can''t find new vocabulary root '..
181             \ string(new_vocab_root)..' in vocabulary roots')
182   endif
183   let vocab_dir = fnamemodify(vocab_dir..substitute(a:vocab, '\.', sep, 'g'), ':~')
184   echo vocab_dir
185   let vocab_file = vocab_dir..sep..fnamemodify(vocab_dir, ':t')..'.factor'
186   echo vocab_file
187   call mkdir(vocab_dir, 'p')
188   return a:cmd..' '..fnameescape(vocab_file)
189 endfunction
190
191 " vim:sw=2:et: