-
Notifications
You must be signed in to change notification settings - Fork 167
Build command with autocomplete for .csproj files
I needed a way to make a specific .csproj instead of every project in the .sln.
So I made it so the <tab>
key should autocomplete to a .csproj.
E.g. typing :Make <tab>
will result in a menu of options, or only one option if only one .csproj was found in the search space:
:Make Content.Shared/Content.Shared.csproj
:Make
BuildChecker/BuildChecker.csproj Content.Shared/Content.Shared.csproj
Content.Server.Database/Content.Server.Database.csproj
See :help wildmenu
.
This approach optionally depends on an Async plugin, either hauleth/asyncdo.vim or an alternative like skywind3000/asyncrun.vim which features real-time population of the QuickFix list.
~/.vim/ftplugin/cs.vim
:
"Reference: https://github.com/OmniSharp/omnisharp-vim/issues/386
compiler cs
" [re]defines :Make command, with autocomplete to a csproj in the sln
command! -bang -nargs=* -bar -complete=custom,s:DotNetFileComplete Make call asyncrun#run(<bang>0, #{program: 'make'}, <f-args>)
" Or alternatively:
command! -bang -nargs=* -bar -complete=custom,s:DotNetFileComplete Make AsyncRun -program=make @ <args>
" For asyncdo instead of AsyncRun:
command! -bang -nargs=* -complete=customlist,DotNetFileComplete Make call asyncdo#run(<bang>0, &makeprg, <f-args>)
" Find relevant .csproj files to populate autocomplete list. See `:help command-completion-custom`
" s:DotNetFileComplete() requires the 'project' data from OmniSharp-Roslyn, without better IPC
" in Vim it is best to cache that data earlier, using s:CacheOmniSharpProjectInBuffer()
function! s:DotNetFileComplete(A,L,P)
let searchdir = expand('%:.:h')
let matches = ''
" If we're not relative to the cwd (e.g. in :help), don't try to search
if fnamemodify(searchdir,':p:h') !=? searchdir
let host = OmniSharp#GetHost(bufnr('%'))
let csprojs = deepcopy(host.job.projects)
let csprojs_relative = map(csprojs, {index, value -> fnamemodify(value['path'], ':.')})
if has_key(host, 'project')
" Make the first project this file is in first in the sln list
let project = fnamemodify(host['project']['MsBuildProject']['Path'], ':.')
let i = index(csprojs_relative, project)
call remove(csprojs_relative, i)
let matches = join(insert(csprojs_relative, project), "\n")
else
let matches = join(csprojs_relative, "\n")
endif
endif
return matches
endfunction
~/.vim/compiler/cs.vim
:
if exists("current_compiler")
finish
endif
let current_compiler = "cs"
" As of Vim 8.2, the distribution includes a cs compiler `help compiler`
" which uses an obsolete frontent, csc. This provides an alternative to
" that obsolete frontend which uses `dotnet build`.
" Consider both dotnet and mono frontends to compiling C#, you may define
" a CompilerSet for mono in compiler/mono.vim if you wish, and use with `:compiler mono`
if exists(":CompilerSet") != 2 " older Vim always used :setlocal
command -nargs=* CompilerSet setlocal <args>
endif
CompilerSet makeprg=dotnet\ build\ /v:q\ /property:GenerateFullPaths=true\ /clp:ErrorsOnly
CompilerSet errorformat=\ %#%f(%l\\\,%c):\ %m
~/.vim/plugin/omnisharp.vim
" Cache the 'project' data from OmniSharp-Roslyn to each cs buffer
augroup cacheOmniSharpProject
autocmd!
autocmd FileType cs call s:CacheOmniSharpProjectInBuffer()
augroup END
function! s:CacheOmniSharpProjectInBuffer()
if &filetype == 'cs'
let bufnr = bufnr('%')
let host = OmniSharp#GetHost(bufnr('%'))
if !has_key(host, 'project')
" 'project' not in cache, must query from OmniSharp-Roslyn, is async
let l:F = {-> 0}
call OmniSharp#actions#project#Get(bufnr, l:F)
endif
endif
endfunction
If you have many .csproj files in your .sln, then maybe setting the vertical option in :help wildmode
may help navigation.
set wildmenu
somewhere in your vimrc.
The DotNetFileComplete
function above could use generalized refinement:
- Maybe some caching of the result/selection and any other args for a given directory in a
tempfile()
. So:Make
no args would prefer the cached choice. Such caching the argument to build to a custom directory "-o /tmp/build/bin/"
Vimspector appears to be the best option for debugging in Vim. Vimspector requires the configuration file .vimspector.json
in order to know what to debug. The following function will use information available from OmniSharp to build said configuration file in the .sln folder:
let s:dir_separator = fnamemodify('.', ':p')[-1 :]
function! WriteVimspectorConfig() abort
let projects = OmniSharp#GetHost().job.projects
let config = { 'configurations': {} }
for project in projects
let config.configurations[project['name']] = {
\ 'adapter': 'netcoredbg',
\ 'configuration': {
\ 'request': 'launch',
\ 'program': project['target'],
\ 'args': [],
\ 'stopAtEntry': v:true
\ }
\}
endfor
let sln_or_dir = OmniSharp#GetHost().sln_or_dir
let slndir = sln_or_dir =~? '\.sln$' ? fnamemodify(sln_or_dir, ':h') : sln_or_dir
let filename = slndir . s:dir_separator . '.vimspector.json'
call writefile([json_encode(config)], filename)
endfunction
- To debug the built executable, call
call vimspector#LaunchWithSettings( #{ configuration: 'project-name' } )
where project-name is the name of the .csproj. - Or
F5
(what the Vimspector continue keymapping is) and select project name from the prompt.