Mercurial > vim-crosoft
comparison autoload/vimcrosoft.vim @ 0:5d2c0db51914
Initial commit
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 17 Sep 2019 13:24:24 -0700 |
parents | |
children | 376f3371c311 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:5d2c0db51914 |
---|---|
1 " vimcrosoft.vim - A wrapper for Visual Studio solutions | |
2 | |
3 " Utilities {{{ | |
4 | |
5 let s:basedir = expand('<sfile>:p:h:h') | |
6 | |
7 function! vimcrosoft#throw(message) | |
8 throw "vimcrosoft: ".a:message | |
9 endfunction | |
10 | |
11 function! vimcrosoft#error(message) | |
12 let v:errmsg = "vimcrosoft: ".a:message | |
13 echoerr v:errmsg | |
14 endfunction | |
15 | |
16 function! vimcrosoft#warning(message) | |
17 echohl WarningMsg | |
18 echom "vimcrosoft: ".a:message | |
19 echohl None | |
20 endfunction | |
21 | |
22 function! vimcrosoft#trace(message) | |
23 if g:vimcrosoft_trace | |
24 echom "vimcrosoft: ".a:message | |
25 endif | |
26 endfunction | |
27 | |
28 function! vimcrosoft#ensure_msbuild_found() abort | |
29 if !empty(g:vimcrosoft_msbuild_path) | |
30 return 1 | |
31 endif | |
32 | |
33 let l:programfilesdir = get(environ(), 'ProgramFiles(x86)') | |
34 let l:vswhere = l:programfilesdir.'\Microsoft Visual Studio\Installer\vswhere.exe' | |
35 if !executable(l:vswhere) | |
36 call vimcrosoft#error("Can't find `vswhere` -- you must set `g:vimcrosoft_msbuild_path` yourself.") | |
37 return 0 | |
38 endif | |
39 | |
40 let l:vswhere_cmdline = '"'.l:vswhere.'" '. | |
41 \'-prerelease -latest -products * '. | |
42 \'-requires Microsoft.Component.MSBuild '. | |
43 \'-property installationPath' | |
44 call vimcrosoft#trace("Trying to find MSBuild, running: ".l:vswhere_cmdline) | |
45 let l:installdirs = split(system(l:vswhere_cmdline), '\n') | |
46 call vimcrosoft#trace("Got: ".string(l:installdirs)) | |
47 for installdir in l:installdirs | |
48 let l:msbuild = installdir.'\MSBuild\Current\Bin\MSBuild.exe' | |
49 if executable(l:msbuild) | |
50 let g:vimcrosoft_msbuild_path = l:msbuild | |
51 return 1 | |
52 endif | |
53 let l:msbuild = installdir.'\MSBuild\15.0\Bin\MSBuild.exe' | |
54 if executable(l:msbuild) | |
55 let g:vimcrosoft_msbuild_path = l:msbuild | |
56 return 1 | |
57 endif | |
58 endfor | |
59 | |
60 call vimcrosoft#error("Couldn't find MSBuild anywhere in:\n". | |
61 \string(l:installdirs)) | |
62 return 0 | |
63 endfunction | |
64 | |
65 " }}} | |
66 | |
67 " Cache Files {{{ | |
68 | |
69 function! vimcrosoft#get_sln_cache_dir(...) abort | |
70 if empty(g:vimcrosoft_current_sln) | |
71 if a:0 && a:1 | |
72 call vimcrosoft#throw("No solution is currently set.") | |
73 endif | |
74 return '' | |
75 endif | |
76 let l:cache_dir = fnamemodify(g:vimcrosoft_current_sln, ':h') | |
77 let l:cache_dir .= '\.vimcrosoft' | |
78 return l:cache_dir | |
79 endfunction | |
80 | |
81 function! vimcrosoft#get_sln_cache_file(filename) abort | |
82 let l:cache_dir = vimcrosoft#get_sln_cache_dir() | |
83 if empty(l:cache_dir) | |
84 return '' | |
85 else | |
86 return l:cache_dir.'\'.a:filename | |
87 endif | |
88 endfunction | |
89 | |
90 " }}} | |
91 | |
92 " Configuration Files {{{ | |
93 | |
94 let s:config_format = 2 | |
95 | |
96 function! vimcrosoft#save_config() abort | |
97 if empty(g:vimcrosoft_current_sln) | |
98 return | |
99 endif | |
100 | |
101 call vimcrosoft#trace("Saving config for: ".g:vimcrosoft_current_sln) | |
102 let l:lines = [ | |
103 \'format='.string(s:config_format), | |
104 \g:vimcrosoft_current_sln, | |
105 \g:vimcrosoft_current_config, | |
106 \g:vimcrosoft_current_platform, | |
107 \g:vimcrosoft_active_project] | |
108 let l:configfile = vimcrosoft#get_sln_cache_file('config.txt') | |
109 call writefile(l:lines, l:configfile) | |
110 endfunction | |
111 | |
112 function! vimcrosoft#load_config() abort | |
113 if empty(g:vimcrosoft_current_sln) | |
114 return | |
115 endif | |
116 | |
117 let l:configfile = vimcrosoft#get_sln_cache_file('config.txt') | |
118 if !filereadable(l:configfile) | |
119 return | |
120 endif | |
121 | |
122 let l:lines = readfile(l:configfile) | |
123 let l:format_line = l:lines[0] | |
124 if l:format_line == 'format='.string(s:config_format) | |
125 let g:vimcrosoft_current_sln = l:lines[1] | |
126 let g:vimcrosoft_current_config = l:lines[2] | |
127 let g:vimcrosoft_current_platform = l:lines[3] | |
128 let g:vimcrosoft_active_project = l:lines[4] | |
129 else | |
130 call vimcrosoft#warning("Solution configuration format has changed ". | |
131 \"since you last opened this solution in Vim. ". | |
132 \"You previous configuration/platform has NOT been ". | |
133 \"restored.") | |
134 endif | |
135 endfunction | |
136 | |
137 " }}} | |
138 | |
139 " {{{ Scripts | |
140 | |
141 let s:scriptsdir = s:basedir.'\scripts' | |
142 | |
143 function! vimcrosoft#get_script_path(scriptname) abort | |
144 return s:scriptsdir.'\'.a:scriptname | |
145 endfunction | |
146 | |
147 function! vimcrosoft#exec_script_job(scriptname, ...) abort | |
148 let l:scriptpath = vimcrosoft#get_script_path(a:scriptname) | |
149 let l:cmd = ['python', l:scriptpath] + a:000 | |
150 return job_start(l:cmd) | |
151 endfunction | |
152 | |
153 let s:scriptsdir_added_to_sys = 0 | |
154 let s:scripts_imported = [] | |
155 | |
156 function! s:install_scriptsdir() abort | |
157 if !s:scriptsdir_added_to_sys | |
158 execute 'python3 import sys' | |
159 execute 'python3 sys.path.append("'.escape(s:scriptsdir, "\\").'")' | |
160 execute 'python3 import vimutil' | |
161 let s:scriptsdir_added_to_sys = 1 | |
162 endif | |
163 endfunction | |
164 | |
165 function! vimcrosoft#exec_script_now(scriptname, ...) abort | |
166 if g:vimcrosoft_use_external_python | |
167 let l:cmd = 'python '.shellescape(vimcrosoft#get_script_path(a:scriptname.'.py')) | |
168 " TODO: shellescape arguments? | |
169 let l:cmd .= ' '.join(a:000, " ") | |
170 let l:output = system(l:cmd) | |
171 else | |
172 call s:install_scriptsdir() | |
173 if index(s:scripts_imported, a:scriptname) < 0 | |
174 execute 'python3 import '.a:scriptname | |
175 call add(s:scripts_imported, a:scriptname) | |
176 endif | |
177 let l:line = 'vimutil.runscript('.a:scriptname.'.main' | |
178 if a:0 > 0 | |
179 let l:args = copy(a:000) | |
180 call map(l:args, {idx, val -> escape(val, '\"')}) | |
181 let l:line .= ', "'.join(l:args, '", "').'"' | |
182 endif | |
183 let l:line .= ')' | |
184 call vimcrosoft#trace("Executing: ".l:line) | |
185 let l:output = py3eval(l:line) | |
186 endif | |
187 return l:output | |
188 endfunction | |
189 | |
190 " }}} | |
191 | |
192 " Module Management {{{ | |
193 | |
194 let s:modulesdir = s:basedir.'\autoload\vimcrosoft' | |
195 let s:modules = glob(s:modulesdir.'\*.vim', 0, 1) | |
196 | |
197 function! vimcrosoft#call_modules(funcname, ...) abort | |
198 for modpath in s:modules | |
199 let l:modname = fnamemodify(modpath, ':t:r') | |
200 let l:fullfuncname = 'vimcrosoft#'.l:modname.'#'.a:funcname | |
201 if exists("*".l:fullfuncname) | |
202 call vimcrosoft#trace("Module ".l:modname.": calling ".a:funcname) | |
203 call call(l:fullfuncname, a:000) | |
204 else | |
205 call vimcrosoft#trace("Skipping ".l:fullfuncname.": doesn't exist.") | |
206 endif | |
207 endfor | |
208 endfunction | |
209 | |
210 " }}} | |
211 | |
212 " Solution Management {{{ | |
213 | |
214 function! vimcrosoft#set_sln(slnpath, ...) abort | |
215 let g:vimcrosoft_current_sln = a:slnpath | |
216 | |
217 let l:sln_was_set = !empty(a:slnpath) | |
218 if l:sln_was_set | |
219 let g:vimcrosoft_current_sln_cache = vimcrosoft#get_sln_cache_file("slncache.bin") | |
220 call vimcrosoft#call_modules('on_sln_changed', a:slnpath) | |
221 else | |
222 let g:vimcrosoft_current_sln_cache = '' | |
223 call vimcrosoft#call_modules('on_sln_cleared') | |
224 endif | |
225 | |
226 let l:silent = a:0 && a:1 | |
227 if !l:silent | |
228 call vimcrosoft#save_config() | |
229 | |
230 if l:sln_was_set | |
231 echom "Current solution: ".a:slnpath | |
232 else | |
233 echom "No current solution anymore" | |
234 endif | |
235 endif | |
236 endfunction | |
237 | |
238 function! vimcrosoft#auto_find_sln(...) abort | |
239 let l:path = getcwd() | |
240 try | |
241 let l:slnpath = vimcrosoft#find_sln(l:path) | |
242 catch /^vimcrosoft:/ | |
243 let l:slnpath = '' | |
244 endtry | |
245 let l:silent = a:0 && a:1 | |
246 call vimcrosoft#set_sln(l:slnpath, l:silent) | |
247 endfunction | |
248 | |
249 function! vimcrosoft#find_sln(curpath) abort | |
250 if g:vimcrosoft_sln_finder != '' | |
251 return call(g:vimcrosoft_sln_finder, [a:curpath]) | |
252 endif | |
253 return vimcrosoft#default_sln_finder(a:curpath) | |
254 endfunction | |
255 | |
256 function! vimcrosoft#default_sln_finder(path) abort | |
257 let l:cur = a:path | |
258 let l:prev = "" | |
259 while l:cur != l:prev | |
260 let l:slnfiles = globpath(l:cur, '*.sln', 0, 1) | |
261 if !empty(l:slnfiles) | |
262 call vimcrosoft#trace("Found solution file: ".l:slnfiles[0]) | |
263 return l:slnfiles[0] | |
264 endif | |
265 let l:prev = l:cur | |
266 let l:cur = fnamemodify(l:cur, ':h') | |
267 endwhile | |
268 call vimcrosoft#throw("No solution file found.") | |
269 endfunction | |
270 | |
271 function! vimcrosoft#set_active_project(projname, ...) abort | |
272 " Strip trailing spaces in the project name. | |
273 let l:projname = substitute(a:projname, '\v\s+$', '', 'g') | |
274 | |
275 let g:vimcrosoft_active_project = l:projname | |
276 call vimcrosoft#call_modules('on_active_project_changed', l:projname) | |
277 | |
278 let l:silent = a:0 && a:1 | |
279 if !l:silent | |
280 call vimcrosoft#save_config() | |
281 echom "Active project changed" | |
282 endif | |
283 endfunction | |
284 | |
285 function! vimcrosoft#build_sln(target) abort | |
286 let l:args = [] | |
287 if !empty(a:target) | |
288 call add(l:args, '/t:'.a:target) | |
289 endif | |
290 call vimcrosoft#run_make(l:args) | |
291 endfunction | |
292 | |
293 function! vimcrosoft#build_project(projname, target, only) abort | |
294 let l:projname = !empty(a:projname) ? a:projname : g:vimcrosoft_active_project | |
295 if empty(l:projname) | |
296 call vimcrosoft#error("No project name given, and no active project set.") | |
297 return | |
298 endif | |
299 | |
300 " Strip trailing spaces in the project name. | |
301 let l:projname = substitute(l:projname, '\v\s+$', '', 'g') | |
302 let l:target = '/t:'.tr(l:projname, '.', '_') | |
303 if !empty(a:target) | |
304 let l:target .= ':'.a:target | |
305 endif | |
306 | |
307 let l:args = [] | |
308 call add(l:args, l:target) | |
309 if a:only | |
310 call add(l:args, '/p:BuildProjectReferences=false') | |
311 endif | |
312 call vimcrosoft#run_make(l:args) | |
313 endfunction | |
314 | |
315 function! vimcrosoft#run_make(customargs) abort | |
316 if !vimcrosoft#ensure_msbuild_found() | |
317 return | |
318 endif | |
319 | |
320 " Add some common arguments for MSBuild. | |
321 let l:fullargs = copy(a:customargs) | |
322 call add(l:fullargs, '"/p:Configuration='.g:vimcrosoft_current_config.'"') | |
323 call add(l:fullargs, '"/p:Platform='.g:vimcrosoft_current_platform.'"') | |
324 " Add the solution file itself. | |
325 call add(l:fullargs, '"'.g:vimcrosoft_current_sln.'"') | |
326 | |
327 " Setup the backdoor args list for our compiler to pick-up, and run | |
328 " the make process. | |
329 let g:vimcrosoft_temp_compiler_args__ = l:fullargs | |
330 compiler vimcrosoftsln | |
331 if !empty(g:vimcrosoft_make_command) | |
332 execute g:vimcrosoft_make_command | |
333 elseif exists(":Make") " Support for vim-dispatch. | |
334 Make | |
335 else | |
336 make | |
337 endif | |
338 endfunction | |
339 | |
340 function! vimcrosoft#set_config_platform(configplatform) | |
341 let l:bits = split(a:configplatform, '|') | |
342 if len(l:bits) != 2 | |
343 call vimcrosoft#throw("Expected a value of the form: Config|Platform") | |
344 endif | |
345 | |
346 let g:vimcrosoft_current_config = l:bits[0] | |
347 let g:vimcrosoft_current_platform = l:bits[1] | |
348 call vimcrosoft#call_modules('on_config_platform_changed', | |
349 \g:vimcrosoft_current_config, g:vimcrosoft_current_platform) | |
350 | |
351 call vimcrosoft#save_config() | |
352 endfunction | |
353 | |
354 function! vimcrosoft#get_sln_project_names() abort | |
355 if empty(g:vimcrosoft_current_sln) | |
356 return [] | |
357 endif | |
358 let l:output = vimcrosoft#exec_script_now("list_sln_projects", | |
359 \g:vimcrosoft_current_sln, | |
360 \'-c', g:vimcrosoft_current_sln_cache, | |
361 \'--full-names') | |
362 return split(l:output, "\n") | |
363 endfunction | |
364 | |
365 function! vimcrosoft#get_sln_config_platforms() abort | |
366 if empty(g:vimcrosoft_current_sln) | |
367 return [] | |
368 endif | |
369 let l:output = vimcrosoft#exec_script_now("list_sln_configs", | |
370 \g:vimcrosoft_current_sln, | |
371 \'-c', g:vimcrosoft_current_sln_cache) | |
372 return split(l:output, "\n") | |
373 endfunction | |
374 | |
375 " }}} | |
376 | |
377 " {{{ Commands Auto-completion | |
378 | |
379 function! vimcrosoft#complete_current_sln_projects(ArgLead, CmdLine, CursorPos) | |
380 let l:proj_names = vimcrosoft#get_sln_project_names() | |
381 let l:argpat = '^'.substitute(a:ArgLead, '\', '', 'g') | |
382 let l:projnames = filter(l:proj_names, | |
383 \{idx, val -> val =~? l:argpat}) | |
384 return l:projnames | |
385 endfunction | |
386 | |
387 function! vimcrosoft#complete_current_sln_config_platforms(ArgLead, CmdLine, CursorPos) | |
388 let l:cfgplats = vimcrosoft#get_sln_config_platforms() | |
389 let l:argpat = '^'.substitute(a:ArgLead, '\', '', 'g') | |
390 let l:cfgplatnames = filter(l:cfgplats, | |
391 \{idx, val -> val =~? l:argpat}) | |
392 return l:cfgplatnames | |
393 endfunction | |
394 | |
395 " }}} | |
396 | |
397 " {{{ Statusline Functions | |
398 | |
399 function! vimcrosoft#statusline(...) | |
400 if empty(g:vimcrosoft_current_sln) | |
401 return '' | |
402 endif | |
403 | |
404 let l:line = fnamemodify(g:vimcrosoft_current_sln, ':t') | |
405 if !empty(g:vimcrosoft_active_project) | |
406 let l:line .= '('.g:vimcrosoft_active_project.')' | |
407 endif | |
408 let l:line .= ' ['. | |
409 \g:vimcrosoft_current_config.'|'. | |
410 \g:vimcrosoft_current_platform.']' | |
411 return l:line | |
412 endfunction | |
413 | |
414 " }}} | |
415 | |
416 " {{{ Initialization | |
417 | |
418 function! vimcrosoft#init() abort | |
419 call vimcrosoft#trace("Loading modules...") | |
420 for modpath in s:modules | |
421 execute 'source '.fnameescape(modpath) | |
422 endfor | |
423 | |
424 call vimcrosoft#call_modules('init') | |
425 | |
426 if g:vimcrosoft_auto_find_sln | |
427 call vimcrosoft#auto_find_sln(1) | |
428 call vimcrosoft#load_config() | |
429 endif | |
430 endfunction | |
431 | |
432 " }}} |