1 " Location: autoload/factor.vim
5 let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/')..'\+'
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..'$', '', '')
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 ? '/' : '\')
18 if !exists('g:FactorGlobEscape')
19 if exists('+shellslash') && !&shellslash
20 let g:FactorGlobEscape = '*[]?`{$'
22 let g:FactorGlobEscape = '*[]?`{$\'
26 " (Based on tpope/vim-scriptease.)
27 function! s:glob(pattern, nosuf = 0, alllinks = 0) abort
29 return glob(a:pattern, a:nosuf, 1, a:alllinks)
31 return split(glob(a:pattern, a:nosuf), "\n")
35 " Section: File discovery & globbing
37 function! factor#get_vocab_roots() abort
38 if exists('g:FactorVocabRoots')
39 return g:FactorVocabRoots
41 if !exists('g:FactorAdditionalVocabRoots')
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 = []
50 let g:FactorVocabRoots =
51 \ map(g:FactorDefaultVocabRoots + g:FactorAdditionalVocabRoots, 's:remove_last_path_sep(v:val)')
52 return g:FactorVocabRoots
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)
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:])
68 call add(expanded_vocab_roots,
69 \ vocab_root =~# '^resource:' ? g:FactorResourcePath..vocab_root[9:] : vocab_root)
72 return expanded_vocab_roots
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
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)
87 let path = fnamemodify(path, ':p')
88 if get(expanded_vocab_roots, path, 0)
89 call add(parent_vocab_roots, path)
92 let current_path = current_path ==# '/' ? '' : fnamemodify(current_path, ':h')
94 return parent_vocab_roots
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
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'))
109 let path = a:output == 0 ? 'resource:'..path[strlen(path_root):] : path
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'))
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')
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'))
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')
150 return sort(keys(found))
153 " Section: Completion
155 function! factor#complete_glob(arg_lead, cmd_line, cursor_pos) abort
156 return factor#glob(a:arg_lead..'*', 0, 1)
159 function! factor#complete_vocab_glob(arg_lead, cmd_line, cursor_pos) abort
160 return factor#glob(a:arg_lead..'*.', 1, 2, 2)
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)
170 return 'echoerr '..string('Factor: Can''t find vocabulary '..a:vocab..' in vocabulary roots')
172 return a:cmd..' '..fnameescape(vocab_file)
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)
180 return 'echoerr '..string('Factor: Can''t find new vocabulary root '..
181 \ string(new_vocab_root)..' in vocabulary roots')
183 let vocab_dir = fnamemodify(vocab_dir..substitute(a:vocab, '\.', sep, 'g'), ':~')
185 let vocab_file = vocab_dir..sep..fnamemodify(vocab_dir, ':t')..'.factor'
187 call mkdir(vocab_dir, 'p')
188 return a:cmd..' '..fnameescape(vocab_file)