diff --git a/README.md b/README.md index 5b713b543..c9f907d51 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ Use Vim's popup windows and neovim's floating windows to display code/documentat ## New! Run unit tests -Using stdio mode, it is now possible to run unit tests via OmniSharp-roslyn, with success/failures listed in the quickfix window for easy navigation: +It is now possible to run unit tests via OmniSharp-roslyn, with success/failures listed in the quickfix window for easy navigation: ```vim -" Run the current unit test (the cursor should be inside the test method) +" Run the current unit test (the cursor should be on/inside the test method) :OmniSharpRunTest " Run all unit tests in the current file @@ -32,26 +32,30 @@ Using stdio mode, it is now possible to run unit tests via OmniSharp-roslyn, wit :OmniSharpRunTestsInFile % tests/test1.cs ``` -Note that this unfortunately does _not_ work in translated WSL, due to the way OmniSharp-roslyn runs the tests. +**Note:** this is only available using the stdio server, and unfortunately does _not_ work in translated WSL, due to the way OmniSharp-roslyn runs the tests. -## New! Asynchronous server interactions +## Asynchronous server interactions over stdio -For vim8 and neovim, OmniSharp-vim can now use the OmniSharp-roslyn stdio server instead of the HTTP server, using pure vimscript (no python dependency!). All server operations are asynchronous and this results in a much smoother coding experience. +For vim8 and neovim, OmniSharp-vim uses the OmniSharp-roslyn stdio server instead of the older HTTP server, using pure vimscript (no python dependency!). All server operations are asynchronous and this results in a much smoother coding experience. -This is initially opt-in only until some [user feedback](https://github.com/OmniSharp/omnisharp-vim/issues/468) is received. To switch from the HTTP server to stdio, add this to your .vimrc: +**Note:** neovim on Windows has been unable to communicate with the stdio server until a (very) recent neovim patch. +If you are using nvim-qt _or_ terminal neovim on Windows, you will require a neovim [nightly build](https://github.com/neovim/neovim/releases/nightly) `NVIM v0.5.0-556` (2020-06-11) or newer, or will need to wait for neovim 0.5 to be released. + +**Note to WSL users:** OmniSharp-roslyn stdio version 1.35.3 does _not_ work from WSL, using the Windows server, see [OmniSharp-roslyn #1844](https://github.com/OmniSharp/omnisharp-roslyn/issues/1844). +Install the previous version with `:OmniSharpInstall v1.35.2` + +To use the HTTP server instead, add this to your .vimrc: ```vim -let g:OmniSharp_server_stdio = 1 +let g:OmniSharp_server_stdio = 0 ``` -Then open vim to a .cs file and install the stdio server with `:OmniSharpInstall`. Restart vim and feel the difference! - -**Note:** neovim on Windows has been unable to communicate with the stdio server until a (very) recent neovim patch. If you are using nvim-qt _or_ terminal neovim on Windows, you will require a neovim [nightly build](https://github.com/neovim/neovim/releases/nightly) `NVIM v0.5.0-556` (2020-06-11) or newer, or will need to wait for neovim 0.5 to be released. +Any time `g:OmniSharp_server_stdio` is modified, the server needs to be re-installed with `:OmniSharpInstall`. ## Features * Contextual code completion - * Code documentation is displayed in the preview window when available (Xml Documentation for Windows, MonoDoc documentation for Mono) + * Code documentation and type lookup, displayed in popups or the preview window, when available (depends on installed SDKs) * Completion Sources are provided for: * [asyncomplete-vim](https://github.com/prabirshrestha/asyncomplete.vim) * [coc.nvim](https://github.com/neoclide/coc.nvim) @@ -69,10 +73,7 @@ Then open vim to a .cs file and install the stdio server with `:OmniSharpInstall * Find all code issues in solution and populate the quickfix window * Fix using statements for the current buffer (sort, remove and add any missing using statements where possible) * Rename refactoring -* Semantic type highlighting -* Lookup type information of an type/variable/method - * Can be printed to the status line or in the preview window - * Displays documentation for an entity when using preview window +* Full semantic highlighting * Code error checking * Code formatter * Run unit tests and navigate to failing assertions @@ -117,23 +118,23 @@ filetype indent plugin on ``` ### Server -OmniSharp-vim depends on the [OmniSharp-Roslyn](https://github.com/OmniSharp/omnisharp-roslyn) server. The first time OmniSharp-vim tries to open a C# file, it will check for the presence of the server, and if not found it will ask if it should be downloaded. Answer 'y' and the latest version will be downloaded and extracted to `~/.cache/omnisharp-vim/omnisharp-roslyn`, ready to use. *Note:* Requires [`curl`](https://curl.haxx.se/) or [`wget`](https://www.gnu.org/software/wget/) on Linux, macOS, Cygwin and WSL. +OmniSharp-vim depends on the [OmniSharp-Roslyn](https://github.com/OmniSharp/omnisharp-roslyn) server. The first time OmniSharp-vim tries to open a C# file, it will check for the presence of the server, and if not found it will ask if it should be downloaded. Answer `y` and the latest version will be downloaded and extracted to `~/.cache/omnisharp-vim/omnisharp-roslyn`, ready to use. *Note:* Requires [`curl`](https://curl.haxx.se/) or [`wget`](https://www.gnu.org/software/wget/) on Linux, macOS, Cygwin and WSL. Running the command `:OmniSharpInstall` in vim will also install/upgrade to the latest OmniSharp-roslyn release. To install a particular release, including pre-releases, specify the version number like this: ```vim -:OmniSharpInstall v1.34.2 +:OmniSharpInstall v1.35.2 ``` -*Note:* These methods depend on the `g:OmniSharp_server_stdio` variable to decide which OmniSharp-roslyn server to download. If you are unsure, try using the new stdio option first, and only fall back to HTTP if you have problems. +*Note:* These methods depend on the `g:OmniSharp_server_stdio` variable to decide which OmniSharp-roslyn server to download. If you are unsure, try using the default stdio option first, and only fall back to HTTP if you have problems. * **vim8.0+ or neovim**: Use the stdio server, it is used asynchronously and there is no python requirement. * **< vim8.0**: Use the HTTP server. Your vim must have python (2 or 3) support, and you'll need either [vim-dispatch](https://github.com/tpope/vim-dispatch) or [vimproc.vim](https://github.com/Shougo/vimproc.vim) to be installed ```vim -" Use the stdio version of OmniSharp-roslyn: +" Use the stdio version of OmniSharp-roslyn - this is the default let g:OmniSharp_server_stdio = 1 " Use the HTTP version of OmniSharp-roslyn: @@ -157,13 +158,17 @@ No special configuration is required for cygwin. The automatic installation scri #### Windows Subsystem for Linux (WSL) OmniSharp-roslyn can function perfectly well in WSL using linux binaries, if the environment is correctly configured (see [OmniSharp-roslyn](https://github.com/OmniSharp/omnisharp-roslyn) for requirements). -However, if you have the .NET Framework installed in Windows, you may have better results using the Windows binaries. To do this, follow the Manual installation instructions above, configure your vimrc to point to the `OmniSharp.exe` file, and let OmniSharp-vim know that you are operating in Cygwin/WSL mode (indicating that file paths need to be translated by OmniSharp-vim from Unix-Windows and back: +However, if you have the .NET Framework installed in Windows, you may have better results using the Windows binaries. +To do this, follow the Manual installation instructions above, configure your vimrc to point to the `OmniSharp.exe` file, and let OmniSharp-vim know that you are operating in Cygwin/WSL mode (indicating that file paths need to be translated by OmniSharp-vim from Unix-Windows and back: ```vim let g:OmniSharp_server_path = '/mnt/c/OmniSharp/omnisharp.win-x64/OmniSharp.exe' let g:OmniSharp_translate_cygwin_wsl = 1 ``` +**Note:** OmniSharp-roslyn stdio version 1.35.3 does _not_ work from WSL, using the Windows server, see [OmniSharp-roslyn #1844](https://github.com/OmniSharp/omnisharp-roslyn/issues/1844). +Install the previous version with `:OmniSharpInstall v1.35.2` + #### Linux and Mac OmniSharp-Roslyn requires Mono on Linux and OSX. The roslyn server [releases](https://github.com/OmniSharp/omnisharp-roslyn/releases) come with an embedded Mono, but this can be overridden to use the installed Mono by setting `g:OmniSharp_server_use_mono` in your vimrc. See [The Mono Project](https://www.mono-project.com/download/stable/) for installation details. @@ -171,12 +176,14 @@ OmniSharp-Roslyn requires Mono on Linux and OSX. The roslyn server [releases](ht let g:OmniSharp_server_use_mono = 1 ``` +Any time `g:OmniSharp_server_use_mono` is modified, the server needs to be re-installed with `:OmniSharpInstall`. + ##### libuv -For the HTTP version, OmniSharp-Roslyn also requires [libuv](http://libuv.org/) on Linux and Mac. This is typically a simple install step, e.g. `brew install libuv` on Mac, `apt-get install libuv1-dev` on debian/Ubuntu, `pacman -S libuv` on arch linux, `dnf install libuv libuv-devel` on Fedora/CentOS, etc. +For the HTTP server, OmniSharp-Roslyn also requires [libuv](http://libuv.org/) on Linux and Mac. This is typically a simple install step, e.g. `brew install libuv` on Mac, `apt-get install libuv1-dev` on debian/Ubuntu, `pacman -S libuv` on arch linux, `dnf install libuv libuv-devel` on Fedora/CentOS, etc. Please note that if your distro has a "dev" package (`libuv1-dev`, `libuv-devel` etc.) then you will probably need it. -**Note:** This is **not** necessary for the stdio version of OmniSharp-roslyn. +**Note:** This is **not** necessary for the default stdio version of OmniSharp-roslyn. ### Install Python (HTTP only) Install the latest version of python 3 ([Python 3.7](https://www.python.org/downloads/release/python-370/)) or 2 ([Python 2.7.15](https://www.python.org/downloads/release/python-2715/)). @@ -188,13 +195,13 @@ Verify that Python is working inside Vim with :echo has('python3') || has('python') ``` -**Note:** If you are using the stdio version of OmniSharp-roslyn, you do not need python. +**Note:** If you are using the default stdio version of OmniSharp-roslyn, you do not need python. ### Asynchronous command execution OmniSharp-vim can start the server only if any of the following criteria is met: * Vim with job control API is used (8.0+) -* [neovim](https://neovim.io) with job control API is used +* neovim with job control API is used * [vim-dispatch](https://github.com/tpope/vim-dispatch) is installed * [vimproc.vim](https://github.com/Shougo/vimproc.vim) is installed @@ -241,13 +248,8 @@ It tries to detect your solution file (.sln) and starts the OmniSharp-roslyn ser In vim8 and neovim, the server is started invisibly by a vim job. In older versions of vim, the server will be started in different ways depending on whether you are using vim-dispatch in tmux, or are using vim-proc, gvim or running vim in a terminal. -This behaviour can be disabled by setting `let g:OmniSharp_start_server = 0` in your vimrc. You can then start the server manually from within vim with `:OmniSharpStartServer`. Alternatively, the server can be manually started from outside vim: - -```sh -[mono] OmniSharp.exe -s (path/to/sln) -``` - -Add `-v` to get extra debugging output from the server. +This behaviour can be disabled by setting `let g:OmniSharp_start_server = 0` in your vimrc. +You can then start the server manually from within vim with `:OmniSharpStartServer`. To get completions, open a C# file from your solution within Vim and press `` (that is ctrl x followed by ctrl o) in Insert mode, or use a completion or autocompletion plugin. @@ -259,10 +261,11 @@ See the [wiki](https://github.com/OmniSharp/omnisharp-vim/wiki) for more custom OmniSharp-roslyn can provide highlighting information about every symbol of the document. To highlight a document, use command `:OmniSharpHighlight`. -To have `.cs` files automatically highlighted when entering a buffer and leaving insert mode, add this to your .vimrc: +By default, `.cs` files are automatically highlighted when entering a buffer and leaving insert mode. +To disable automatic highlighting, add this to your .vimrc: ```vim -let g:OmniSharp_highlighting = 2 +let g:OmniSharp_highlighting = 0 ``` To update highlighting after all text changes, even while in insert mode, use `g:OmniSharp_highlighting = 3` instead. @@ -280,7 +283,8 @@ let g:OmniSharp_highlight_groups = { The `:OmniSharpHighlightEcho` command can be used to find out what type of symbol is under the cursor. See the [wiki](https://github.com/OmniSharp/omnisharp-vim/wiki/Highlighting-configuration) for the full list of symbol types, and configuration details. -**Note:** Text property highlighting is only available when using the stdio server, not for HTTP server usage. Check the [wiki](https://github.com/OmniSharp/omnisharp-vim/wiki/Highlighting-configuration#legacy-highlighting) for how to highlight when using the HTTP server, or older Vim/neovims. +**Note:** Full semantic highlighting uses Vim's text properties and neovim's namespaces, and is only available when using the stdio server, not for HTTP server usage. +Check the [wiki](https://github.com/OmniSharp/omnisharp-vim/wiki/Highlighting-configuration#legacy-highlighting) for how to configure the simpler regex-highlighting when using the HTTP server, or older Vim/neovims. ## Diagnostics @@ -313,12 +317,15 @@ let g:OmniSharp_diagnostic_showid = 1 *Note:* Diagnostic overrides are only available in stdio mode, not HTTP mode. -Another method for filtering out diagnostic results is via path exclusion using `g:OmniSharp_diagnostic_exclude_paths`. This variable is a list of regular expressions that will exclude paths that have a match to any of its entries. +Another method for filtering out diagnostic results is via path exclusion using `g:OmniSharp_diagnostic_exclude_paths`. +This variable is a list of regular expressions that will exclude paths that have a match to any of its entries: + ```vim let g:OmniSharp_diagnostic_exclude_paths = [ \ 'obj\\', \ '[Tt]emp\\', -\ '\.nuget\\' +\ '\.nuget\\', +\ '\' \] ``` @@ -432,94 +439,66 @@ Plug 'w0rp/ale' call plug#end() endif -" Note: this is required for the plugin to work -filetype indent plugin on - -" Use the stdio OmniSharp-roslyn server -let g:OmniSharp_server_stdio = 1 - -" Set the type lookup function to use the preview window instead of echoing it -"let g:OmniSharp_typeLookupInPreview = 1 - -" Timeout in seconds to wait for a response from the server -let g:OmniSharp_timeout = 5 - " Don't autoselect first omnicomplete option, show options even if there is only " one (so the preview documentation is accessible). Remove 'preview', 'popup' " and 'popuphidden' if you don't want to see any documentation whatsoever. -" Note that neovim does not support `popuphidden` or `popup` yet: +" Note that neovim does not support `popuphidden` or `popup` yet: " https://github.com/neovim/neovim/issues/10996 -set completeopt=longest,menuone,preview,popuphidden - -" Highlight the completion documentation popup background/foreground the same as -" the completion menu itself, for better readability with highlighted -" documentation. -set completepopup=highlight:Pmenu,border:off - -" Fetch full documentation during omnicomplete requests. -" By default, only Type/Method signatures are fetched. Full documentation can -" still be fetched when you need it with the :OmniSharpDocumentation command. -let g:omnicomplete_fetch_full_documentation = 1 - -" Set desired preview window height for viewing documentation. -" You might also want to look at the echodoc plugin. -set previewheight=5 +if has('patch-8.1.1880') + set completeopt=longest,menuone,popuphidden + " Highlight the completion documentation popup background/foreground the same as + " the completion menu itself, for better readability with highlighted + " documentation. + set completepopup=highlight:Pmenu,border:off +else + set completeopt=longest,menuone,preview + " Set desired preview window height for viewing documentation. + set previewheight=5 +endif " Tell ALE to use OmniSharp for linting C# files, and no other linters. let g:ale_linters = { 'cs': ['OmniSharp'] } -" Update semantic highlighting on BufEnter, InsertLeave and TextChanged -let g:OmniSharp_highlighting = 2 - augroup omnisharp_commands - autocmd! - - " Show type information automatically when the cursor stops moving. - " Note that the type is echoed to the Vim command line, and will overwrite - " any other messages in this space including e.g. ALE linting messages. - autocmd CursorHold *.cs OmniSharpTypeLookup - - " The following commands are contextual, based on the cursor position. - autocmd FileType cs nnoremap gd :OmniSharpGotoDefinition - autocmd FileType cs nnoremap fi :OmniSharpFindImplementations - autocmd FileType cs nnoremap fs :OmniSharpFindSymbol - autocmd FileType cs nnoremap fu :OmniSharpFindUsages - - " Finds members in the current buffer - autocmd FileType cs nnoremap fm :OmniSharpFindMembers - - autocmd FileType cs nnoremap fx :OmniSharpFixUsings - autocmd FileType cs nnoremap tt :OmniSharpTypeLookup - autocmd FileType cs nnoremap dc :OmniSharpDocumentation - autocmd FileType cs nnoremap :OmniSharpSignatureHelp - autocmd FileType cs inoremap :OmniSharpSignatureHelp - - " Navigate up and down by method/property/field - autocmd FileType cs nnoremap :OmniSharpNavigateUp - autocmd FileType cs nnoremap :OmniSharpNavigateDown - - " Find all code errors/warnings for the current solution and populate the quickfix window - autocmd FileType cs nnoremap cc :OmniSharpGlobalCodeCheck + autocmd! + + " Show type information automatically when the cursor stops moving. + " Note that the type is echoed to the Vim command line, and will overwrite + " any other messages in this space including e.g. ALE linting messages. + autocmd CursorHold *.cs OmniSharpTypeLookup + + " The following commands are contextual, based on the cursor position. + autocmd FileType cs nmap gd (omnisharp_go_to_definition) + autocmd FileType cs nmap osfu (omnisharp_find_usages) + autocmd FileType cs nmap osfi (omnisharp_find_implementations) + autocmd FileType cs nmap ospd (omnisharp_preview_definition) + autocmd FileType cs nmap ospi (omnisharp_preview_implementations) + autocmd FileType cs nmap ost (omnisharp_type_lookup) + autocmd FileType cs nmap osd (omnisharp_documentation) + autocmd FileType cs nmap osfs (omnisharp_find_symbol) + autocmd FileType cs nmap osfx (omnisharp_fix_usings) + autocmd FileType cs nmap (omnisharp_signature_help) + autocmd FileType cs imap (omnisharp_signature_help) + + " Navigate up and down by method/property/field + autocmd FileType cs nmap [[ (omnisharp_navigate_up) + autocmd FileType cs nmap ]] (omnisharp_navigate_down) + " Find all code errors/warnings for the current solution and populate the quickfix window + autocmd FileType cs nmap osgcc (omnisharp_global_code_check) + " Contextual code actions (uses fzf, CtrlP or unite.vim when available) + autocmd FileType cs nmap osca (omnisharp_code_actions) + autocmd FileType cs xmap osca (omnisharp_code_actions) + + autocmd FileType cs nmap os= (omnisharp_code_format) + + autocmd FileType cs nmap osnm (omnisharp_rename) + + autocmd FileType cs nmap osre (omnisharp_restart_server) + autocmd FileType cs nmap osst (omnisharp_start_server) + autocmd FileType cs nmap ossp (omnisharp_stop_server) augroup END -" Contextual code actions (uses fzf, CtrlP or unite.vim when available) -nnoremap :OmniSharpGetCodeActions -" Run code actions with text selected in visual mode to extract method -xnoremap :call OmniSharp#actions#codeactions#Get('visual') - -" Rename with dialog -nnoremap nm :OmniSharpRename -nnoremap :OmniSharpRename -" Rename without dialog - with cursor on the symbol to rename: `:Rename newname` -command! -nargs=1 Rename :call OmniSharp#actions#rename#To("") - -nnoremap cf :OmniSharpCodeFormat - -" Start the omnisharp server for the current solution -nnoremap ss :OmniSharpStartServer -nnoremap sp :OmniSharpStopServer - -" Enable snippet completion +" Enable snippet completion, using the ultisnips plugin " let g:OmniSharp_want_snippet=1 ``` diff --git a/autoload/OmniSharp.vim b/autoload/OmniSharp.vim index a740cf279..6354e5afa 100644 --- a/autoload/OmniSharp.vim +++ b/autoload/OmniSharp.vim @@ -6,34 +6,39 @@ set cpoptions&vim if !g:OmniSharp_server_stdio " Load python helper functions call OmniSharp#py#Bootstrap() - let g:OmniSharp_py_err = {} endif function! OmniSharp#GetHost(...) abort let bufnr = a:0 ? a:1 : bufnr('%') - if empty(getbufvar(bufnr, 'OmniSharp_host')) - let sln_or_dir = OmniSharp#FindSolutionOrDir(1, bufnr) - if g:OmniSharp_server_stdio - let host = { - \ 'job': OmniSharp#proc#GetJob(sln_or_dir), - \ 'sln_or_dir': sln_or_dir - \} - else + if g:OmniSharp_server_stdio + " Using the stdio server, b:OmniSharp_host is a dict containing the + " `sln_or_dir` and an `initialized` flag indicating whether this buffer has + " successfully been registered with the server: + " { 'sln_or_dir': '/path/to/solution_or_dir', 'initialized': 1 } + let host = getbufvar(bufnr, 'OmniSharp_host', {}) + if get(host, 'sln_or_dir', '') ==# '' + let host.sln_or_dir = OmniSharp#FindSolutionOrDir(1, bufnr) + let host.initialized = 0 + call setbufvar(bufnr, 'OmniSharp_host', host) + endif + " The returned dict includes the job, but the job is _not_ part of + " b:OmniSharp_host. It is important to always fetch the job from + " OmniSharp#proc#GetJob, ensuring that the job properties (job.job_id, + " job.loaded, job.pid etc.) are always correct and up-to-date. + return extend(copy(host), { 'job': OmniSharp#proc#GetJob(host.sln_or_dir) }) + else + " Using the HTTP server, b:OmniSharp_host is a localhost URL + if empty(getbufvar(bufnr, 'OmniSharp_host')) + let sln_or_dir = OmniSharp#FindSolutionOrDir(1, bufnr) let port = OmniSharp#py#GetPort(sln_or_dir) if port == 0 return '' endif let host = get(g:, 'OmniSharp_host', 'http://localhost:' . port) + call setbufvar(bufnr, 'OmniSharp_host', host) endif - call setbufvar(bufnr, 'OmniSharp_host', host) + return getbufvar(bufnr, 'OmniSharp_host') endif - if g:OmniSharp_server_stdio - let host = getbufvar(bufnr, 'OmniSharp_host') - if !OmniSharp#proc#IsJobRunning(host.job) - let host.job = OmniSharp#proc#GetJob(host.sln_or_dir) - endif - endif - return getbufvar(bufnr, 'OmniSharp_host') endfunction @@ -295,6 +300,12 @@ function! OmniSharp#StartServer(...) abort " Optionally perform check if server is already running if check_is_running + let job = OmniSharp#proc#GetJob(sln_or_dir) + if type(job) == type({}) && get(job, 'stopped') + " The job has been manually stopped - do not start it again until + " instructed + return + endif let running = OmniSharp#proc#IsJobRunning(sln_or_dir) if !g:OmniSharp_server_stdio " If the port is hardcoded, we should check if any other vim instances @@ -324,10 +335,9 @@ function! s:StartServer(sln_or_dir) abort return endif - let job = OmniSharp#proc#Start(command, a:sln_or_dir) + call OmniSharp#proc#Start(command, a:sln_or_dir) if g:OmniSharp_server_stdio let b:OmniSharp_host = { - \ 'job': job, \ 'sln_or_dir': a:sln_or_dir \} endif diff --git a/autoload/OmniSharp/actions/buffer.vim b/autoload/OmniSharp/actions/buffer.vim index 863020e22..fbd611788 100644 --- a/autoload/OmniSharp/actions/buffer.vim +++ b/autoload/OmniSharp/actions/buffer.vim @@ -5,13 +5,17 @@ set cpoptions&vim " returned (synchronously or asynchronously) function! OmniSharp#actions#buffer#Update(...) abort let opts = a:0 && a:1 isnot 0 ? { 'Callback': a:1 } : {} - if !OmniSharp#IsServerRunning() | return | endif + let initializing = a:0 > 1 if bufname('%') ==# '' || OmniSharp#FugitiveCheck() | return | endif - if b:changedtick != get(b:, 'OmniSharp_UpdateChangeTick', -1) + if initializing || b:changedtick != get(b:, 'OmniSharp_UpdateChangeTick', -1) let b:OmniSharp_UpdateChangeTick = b:changedtick if g:OmniSharp_server_stdio + if initializing + let opts.Initializing = 1 + endif call s:StdioUpdate(opts) else + if !OmniSharp#IsServerRunning() | return | endif call OmniSharp#py#Eval('updateBuffer()') call OmniSharp#py#CheckForError() if has_key(opts, 'Callback') @@ -25,6 +29,9 @@ function! s:StdioUpdate(opts) abort let opts = { \ 'ResponseHandler': function('s:StdioUpdateRH', [a:opts]) \} + if has_key(a:opts, 'Initializing') + let opts.Initializing = 1 + endif call OmniSharp#stdio#Request('/updatebuffer', opts) endfunction diff --git a/autoload/OmniSharp/actions/diagnostics.vim b/autoload/OmniSharp/actions/diagnostics.vim index 0aeb50223..a1552070e 100644 --- a/autoload/OmniSharp/actions/diagnostics.vim +++ b/autoload/OmniSharp/actions/diagnostics.vim @@ -15,7 +15,7 @@ function! OmniSharp#actions#diagnostics#Check(...) abort endif if g:OmniSharp_server_stdio let Callback = function('s:CBCodeCheck', [opts]) - call OmniSharp#actions#diagnostics#StdioCheck({}, Callback) + call OmniSharp#actions#diagnostics#StdioCheck(bufnr('%'), Callback) else let codecheck = OmniSharp#py#Eval('codeCheck()') if OmniSharp#py#CheckForError() | return | endif @@ -27,7 +27,12 @@ function! OmniSharp#actions#diagnostics#CheckGlobal(...) abort if bufname('%') ==# '' || OmniSharp#FugitiveCheck() | return [] | endif " Place the results in the quickfix window, if possible if g:OmniSharp_server_stdio - call s:StdioCheckGlobal(function('s:CBGlobalCodeCheck')) + let Callback = function('s:CBGlobalCodeCheck') + let opts = { + \ 'ResponseHandler': function('s:StdioCheckRH', [Callback]) + \} + let job = OmniSharp#GetHost().job + call OmniSharp#stdio#RequestGlobal(job, '/codecheck', opts) else let quickfixes = OmniSharp#py#Eval('globalCodeCheck()') if OmniSharp#py#CheckForError() | return | endif @@ -38,26 +43,19 @@ endfunction " Normally this function would be named 's:StdioCheck`, but it is accessed " directly from autoload/ale/sources/OmniSharp.vim so requires a full autoload " function name. -function! OmniSharp#actions#diagnostics#StdioCheck(opts, Callback) abort +function! OmniSharp#actions#diagnostics#StdioCheck(bufnr, Callback) abort let opts = { \ 'ResponseHandler': function('s:StdioCheckRH', [a:Callback]), + \ 'BufNum': a:bufnr, \ 'ReplayOnLoad': 1 \} - call extend(opts, a:opts, 'force') call OmniSharp#stdio#Request('/codecheck', opts) endfunction -function! s:StdioCheckGlobal(Callback) abort - let opts = { - \ 'ResponseHandler': function('s:StdioCheckRH', [a:Callback]) - \} - call OmniSharp#stdio#RequestSend({}, '/codecheck', opts) -endfunction - function! s:StdioCheckRH(Callback, response) abort if !a:response.Success | return | endif call a:Callback(OmniSharp#locations#Parse(a:response.Body.QuickFixes, - \ function("s:DiagnosticQuickfixFixup"))) + \ function('s:DiagnosticQuickfixFixup'))) endfunction function! s:DiagnosticQuickfixFixup(quickfix) abort @@ -65,16 +63,22 @@ function! s:DiagnosticQuickfixFixup(quickfix) abort if len(exclude_paths) && has_key(a:quickfix, 'FileName') for exclude_path in exclude_paths if match(a:quickfix.FileName, exclude_path) > 0 - return + return {} endif endfor endif let overrides = get(g:, 'OmniSharp_diagnostic_overrides', {}) let diag_id = get(a:quickfix, 'Id', '-') - if index(keys(overrides), diag_id) >= 0 + if diag_id =~# '.FadeOut$' + " Some analyzers such as roslynator provide 2 diagnostics: one to mark + " the start of the issue location and another to mark the end, e.g. + " `RCS1124FadeOut`. We never make use of these FadeOut diagnostics, as + " we can extract start and end locations from the main diagnostic. + return {} + elseif index(keys(overrides), diag_id) >= 0 if overrides[diag_id].type ==? 'None' - return + return {} endif call extend(a:quickfix, overrides[diag_id]) endif diff --git a/autoload/OmniSharp/actions/highlight.vim b/autoload/OmniSharp/actions/highlight.vim index 722f190d7..ffd69f260 100644 --- a/autoload/OmniSharp/actions/highlight.vim +++ b/autoload/OmniSharp/actions/highlight.vim @@ -1,12 +1,12 @@ let s:save_cpo = &cpoptions set cpoptions&vim -function! OmniSharp#actions#highlight#Buffer() abort - if bufname('%') ==# '' || OmniSharp#FugitiveCheck() | return | endif - let opts = { 'BufNum': bufnr('%') } +function! OmniSharp#actions#highlight#Buffer(...) abort + let bufnr = a:0 ? a:1 : bufnr('%') + if bufname(bufnr) ==# '' || OmniSharp#FugitiveCheck() | return | endif if g:OmniSharp_server_stdio && \ (has('textprop') || exists('*nvim_create_namespace')) - call s:StdioHighlight(opts.BufNum) + call s:StdioHighlight(bufnr) else " Full semantic highlighting not supported - highlight types instead call OmniSharp#actions#highlight_types#Buffer() @@ -17,6 +17,7 @@ function! s:StdioHighlight(bufnr) abort let buftick = getbufvar(a:bufnr, 'changedtick') let opts = { \ 'ResponseHandler': function('s:HighlightRH', [a:bufnr, buftick]), + \ 'BufNum': a:bufnr, \ 'ReplayOnLoad': 1 \} call OmniSharp#stdio#Request('/v2/highlight', opts) diff --git a/autoload/OmniSharp/actions/highlight_types.vim b/autoload/OmniSharp/actions/highlight_types.vim index a7e47f316..4e457bf2b 100644 --- a/autoload/OmniSharp/actions/highlight_types.vim +++ b/autoload/OmniSharp/actions/highlight_types.vim @@ -12,22 +12,23 @@ set cpoptions&vim function! OmniSharp#actions#highlight_types#Buffer() abort if bufname('%') ==# '' || OmniSharp#FugitiveCheck() | return | endif call OmniSharp#actions#highlight_types#Initialise() - let opts = { 'BufNum': bufnr('%') } + let bufnr = bufnr('%') if g:OmniSharp_server_stdio - let Callback = function('s:CBHighlightBuffer', [opts]) - call s:StdioFindHighlightTypes(Callback) + let Callback = function('s:CBHighlightBuffer', [bufnr]) + call s:StdioFindHighlightTypes(bufnr, Callback) else if !OmniSharp#IsServerRunning() | return | endif let hltypes = OmniSharp#py#Eval('findHighlightTypes()') if OmniSharp#py#CheckForError() | return | endif - call s:CBHighlightBuffer(opts, hltypes) + call s:CBHighlightBuffer(bufnr, hltypes) endif endfunction -function! s:StdioFindHighlightTypes(Callback) abort +function! s:StdioFindHighlightTypes(bufnr, Callback) abort let bufferLines = getline(1, '$') let opts = { \ 'ResponseHandler': function('s:FindHighlightTypesRH', [a:Callback, bufferLines]), + \ 'BufNum': a:bufnr, \ 'ReplayOnLoad': 1 \} call OmniSharp#stdio#Request('/highlight', opts) @@ -68,12 +69,15 @@ function! s:FindHighlightTypesRH(Callback, bufferLines, response) abort call a:Callback(hltypes) endfunction -function! s:CBHighlightBuffer(opts, hltypes) abort +function! s:CBHighlightBuffer(bufnr, hltypes) abort if has_key(a:hltypes, 'error') echohl WarningMsg | echom a:hltypes.error | echohl None return endif - if bufnr('%') != a:opts.BufNum | return | endif + " matchadd() only works in the current window/buffer, so if the user has + " navigated away from the buffer where the request was made, this response can + " not be applied + if bufnr('%') != a:bufnr | return | endif let b:OmniSharp_hl_matches = get(b:, 'OmniSharp_hl_matches', []) diff --git a/autoload/OmniSharp/actions/signature.vim b/autoload/OmniSharp/actions/signature.vim index 074fa00f4..b02f75ab4 100644 --- a/autoload/OmniSharp/actions/signature.vim +++ b/autoload/OmniSharp/actions/signature.vim @@ -10,7 +10,7 @@ function! OmniSharp#actions#signature#SignatureHelp(...) abort else let response = OmniSharp#py#Eval('signatureHelp()') if OmniSharp#py#CheckForError() | return | endif - call s:CBSignatureHelp(response) + call s:CBSignatureHelp(opts, response) endif endfunction diff --git a/autoload/OmniSharp/actions/usages.vim b/autoload/OmniSharp/actions/usages.vim index bc819c4e6..e30ef37db 100644 --- a/autoload/OmniSharp/actions/usages.vim +++ b/autoload/OmniSharp/actions/usages.vim @@ -18,7 +18,10 @@ endfunction function! s:StdioFind(Callback) abort let opts = { - \ 'ResponseHandler': function('s:StdioFindRH', [a:Callback]) + \ 'ResponseHandler': function('s:StdioFindRH', [a:Callback]), + \ 'Parameters': { + \ 'ExcludeDefinition': 1 + \ } \} call OmniSharp#stdio#Request('/findusages', opts) endfunction diff --git a/autoload/OmniSharp/actions/workspace.vim b/autoload/OmniSharp/actions/workspace.vim new file mode 100644 index 000000000..1bb592107 --- /dev/null +++ b/autoload/OmniSharp/actions/workspace.vim @@ -0,0 +1,38 @@ +let s:save_cpo = &cpoptions +set cpoptions&vim + +let s:attempts = 0 + +function! OmniSharp#actions#workspace#Get(job) abort + let opts = { + \ 'ResponseHandler': function('s:ProjectsRH', [a:job]) + \} + let s:attempts += 1 + call OmniSharp#stdio#RequestGlobal(a:job, '/projects', opts) +endfunction + +function! s:ProjectsRH(job, response) abort + " If this request fails, retry up to 5 times + if !a:response.Success + if s:attempts < 5 + call OmniSharp#actions#workspace#Get(a:job) + endif + return + endif + " If no projects have been loaded by the time this callback is reached, there + " are no projects and the job can be marked as ready + let projects = get(get(a:response.Body, 'MsBuild', {}), 'Projects', {}) + let a:job.projects = map(projects, + \ {_,project -> {"name": project.AssemblyName, "path": project.Path}}) + if get(a:job, 'projects_total', 0) > 0 + call OmniSharp#log#Log(a:job, 'Workspace complete: ' . a:job.projects_total . ' project(s)') + else + call OmniSharp#log#Log(a:job, 'Workspace complete: no projects') + call OmniSharp#project#RegisterLoaded(a:job) + endif +endfunction + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim:et:sw=2:sts=2 diff --git a/autoload/OmniSharp/buffer.vim b/autoload/OmniSharp/buffer.vim index 8dbdc8f36..62a32dff8 100644 --- a/autoload/OmniSharp/buffer.vim +++ b/autoload/OmniSharp/buffer.vim @@ -1,6 +1,32 @@ let s:save_cpo = &cpoptions set cpoptions&vim +function! OmniSharp#buffer#Initialize(job, bufnr, command, opts) abort + let a:job.pending_requests = get(a:job, 'pending_requests', {}) + let host = getbufvar(a:bufnr, 'OmniSharp_host') + if get(host, 'initialized') | return | endif + let a:job.pending_requests[a:bufnr] = get(a:job.pending_requests, a:bufnr, {}) + " More recent requests to the same command replace older pending requests + let a:job.pending_requests[a:bufnr][a:command] = a:opts + if has_key(OmniSharp#GetHost(a:bufnr), 'initializing') | return | endif + let host.initializing = 1 + let Callback = function('s:CBInitialize', [a:job, a:bufnr, host]) + call OmniSharp#actions#buffer#Update(Callback, 1) +endfunction + +function! s:CBInitialize(job, bufnr, host) abort + let a:host.initialized = 1 + unlet a:host.initializing + call OmniSharp#log#Log(a:job, 'Replaying requests for buffer ' . a:bufnr) + for key in keys(a:job.pending_requests[a:bufnr]) + call OmniSharp#stdio#Request(key, a:job.pending_requests[a:bufnr][key]) + unlet a:job.pending_requests[a:bufnr][key] + if empty(a:job.pending_requests[a:bufnr]) + unlet a:job.pending_requests[a:bufnr] + endif + endfor +endfunction + function! OmniSharp#buffer#PerformChanges(opts, response) abort if !a:response.Success | return | endif let changes = get(a:response.Body, 'Changes', []) diff --git a/autoload/OmniSharp/locations.vim b/autoload/OmniSharp/locations.vim index 016182bfa..1ef023c04 100644 --- a/autoload/OmniSharp/locations.vim +++ b/autoload/OmniSharp/locations.vim @@ -27,7 +27,7 @@ function! OmniSharp#locations#Parse(quickfixes, ...) abort let locations = [] for quickfix in a:quickfixes let quickfix = a:0 ? a:1(quickfix) : quickfix - if type(quickfix) == 0 + if empty(quickfix) continue endif @@ -50,7 +50,6 @@ function! OmniSharp#locations#Parse(quickfixes, ...) abort let location.end_col = quickfix.EndColumn - 1 endif - if has_key(quickfix, 'type') let location.type = get(quickfix, 'type') if has_key(quickfix, 'subtype') diff --git a/autoload/OmniSharp/log.vim b/autoload/OmniSharp/log.vim index ba2e18518..b40ea7b8d 100644 --- a/autoload/OmniSharp/log.vim +++ b/autoload/OmniSharp/log.vim @@ -3,32 +3,115 @@ set cpoptions&vim let s:stdiologfile = expand(':p:h:h:h') . '/log/stdio.log' -function! OmniSharp#log#Log(message, loglevel) abort - let logit = 0 - if g:OmniSharp_loglevel ==? 'debug' - " Log everything - let logit = 1 - elseif g:OmniSharp_loglevel ==? 'info' - let logit = a:loglevel ==# 'info' - else - " g:OmniSharp_loglevel ==? 'none' +" Log from OmniSharp-vim +function! OmniSharp#log#Log(job, message, ...) abort + if g:OmniSharp_loglevel ==? 'none' | return | endif + call s:Init(a:job) + let debug = a:0 && a:1 + if g:OmniSharp_loglevel !=? 'info' || !debug + call writefile([a:message], a:job.logfile, 'a') endif - if logit - call writefile([a:message], s:stdiologfile, 'a') +endfunction + +" Log a decoded server message +function! OmniSharp#log#LogServer(job, raw, msg) abort + if g:OmniSharp_loglevel ==? 'none' | return | endif + call s:Init(a:job) + if get(g:, 'OmniSharp_proc_debug') + call writefile([a:raw], a:job.logfile, 'a') + elseif !has_key(a:msg, 'Body') || type(a:msg.Body) != type({}) + return + elseif !has_key(a:msg, 'Event') + return + elseif get(a:msg, 'Event', '') ==# 'log' + " Attempt to normalise newlines, which can be \%uD\%u0 in Windows and \%u0 + " in linux + let message = substitute(a:msg.Body.Message, '\%uD\ze\%u0', '', 'g') + let lines = split(message, '\%u0', 1) + if g:OmniSharp_loglevel ==# 'DEBUG' && lines[0] =~# '^\*\{12\}' + " Special loglevel - DEBUG all caps. This still tells the server to pass + " full debugging requests and responses plus debugging messages, but + " OmniSharp-vim will not log the requests and responses - just record + " their commands + let prefix = matchstr(lines[0], '\*\s\+\zs\S\+\ze\s\+\*') + let commands = filter(lines, "v:val =~# '^\\s*\"Command\":'") + if len(commands) + let command = matchstr(commands[0], '"Command": "\zs[^"]\+\ze"') + let command_name = printf('Server %s: %s', prefix, command) + call writefile([command_name], a:job.logfile, 'a') + endif + else + let lines[0] = ' ' . lines[0] + let prefix = s:LogLevelPrefix(a:msg.Body.LogLevel) + call insert(lines, printf('[%s]: %s', prefix, a:msg.Body.Name)) + call writefile(lines, a:job.logfile, 'a') + endif + elseif get(a:msg, 'Event', '') ==# 'MsBuildProjectDiagnostics' + if len(a:msg.Body.Errors) == 0 && len(a:msg.Body.Warnings) == 0 + return + endif + let lines = [a:msg.Body.FileName] + for error in a:msg.Body.Errors + call add(lines, printf('%s(%d,%d): Error: %s', + \ error.FileName, error.StartLine, error.StartColumn, error.Text)) + endfor + for warn in a:msg.Body.Warnings + call add(lines, printf('%s(%d,%d): Warning: %s', + \ warn.FileName, warn.StartLine, warn.StartColumn, warn.Text)) + endfor + call writefile(lines, a:job.logfile, 'a') endif endfunction function! OmniSharp#log#Open(...) if g:OmniSharp_server_stdio let logfile = s:stdiologfile + if exists('b:OmniSharp_host') + let job = OmniSharp#GetHost().job + if type(job) == type({}) + let logfile = get(job, 'logfile', s:stdiologfile) + endif + endif else let logfile = OmniSharp#py#Eval('getLogFile()') if OmniSharp#py#CheckForError() | return | endif endif let cmd = (a:0 && type(a:1) == type('') && len(a:1)) ? a:1 : 'edit' + if cmd ==# 'edit' && !&hidden + let cmd = 'split' + endif exec cmd logfile endfunction +function! s:LogLevelPrefix(loglevel) abort + if a:loglevel ==# 'TRACE' + return 'trce' + elseif a:loglevel ==# 'DEBUG' + return 'dbug' + elseif a:loglevel ==# 'INFORMATION' + return 'info' + elseif a:loglevel ==# 'WARNING' + return 'warn' + elseif a:loglevel ==# 'ERROR' + return 'fail' + elseif a:loglevel ==# 'CRITICAL' + return 'crit' + else + return a:loglevel + endif +endfunction + +function! s:Init(job) abort + if has_key(a:job, 'logfile') + return + endif + let logfile = strftime('%Y%m%d%H%M_') . get(a:job, 'pid') . '_omnisharp.log' + let logfile = fnamemodify(s:stdiologfile, ':h') . '/' . logfile + " Add the new log filename to the standard log, so it can be opened with `gf` + call writefile([logfile], s:stdiologfile, 'a') + let a:job.logfile = logfile +endfunction + let &cpoptions = s:save_cpo unlet s:save_cpo diff --git a/autoload/OmniSharp/proc.vim b/autoload/OmniSharp/proc.vim index bf3e32839..47ebbb502 100644 --- a/autoload/OmniSharp/proc.vim +++ b/autoload/OmniSharp/proc.vim @@ -37,10 +37,14 @@ function! OmniSharp#proc#neovimOutHandler(job_id, data, event) dict abort endfunction function! OmniSharp#proc#neovimErrHandler(job_id, data, event) dict abort - if type(a:data) == type([]) && len(a:data) && a:data[0] =~# "^\uFEFF$" + if type(a:data) == type([]) && len(a:data) == 1 && a:data[0] =~# "^\uFEFF$" " Ignore BOM return endif + if type(a:data) == type([]) && len(a:data) == 1 && a:data[0] ==# '' + " Ignore empty + return + endif let message = printf('%s: %s', a:event, string(a:data)) call OmniSharp#util#EchoErr(message) endfunction @@ -53,9 +57,6 @@ function! OmniSharp#proc#neovimExitHandler(job_id, data, event) dict abort break endif endfor - if !empty(jobkey) && has_key(s:jobs, jobkey) - call remove(s:jobs, jobkey) - endif endfunction function! OmniSharp#proc#neovimJobStart(command) abort @@ -75,7 +76,9 @@ function! OmniSharp#proc#neovimJobStart(command) abort \ 'job_id': jobstart(a:command, opts), \ 'partial': '' \} + let job.pid = jobpid(job.job_id) let s:channels[job.job_id] = job + call OmniSharp#log#Log(job, split(execute('version'), "\n")[0]) return job endfunction @@ -92,7 +95,13 @@ function! OmniSharp#proc#vimOutHandler(channel, message) abort echom printf('%s: %s', string(a:channel), string(a:message)) endif if g:OmniSharp_server_stdio - call OmniSharp#stdio#HandleResponse(s:channels[a:channel], a:message) + let message = a:message + if message =~# "^\uFEFF" + " Strip BOM + let message = substitute(message, "^\uFEFF", '', '') + endif + let job = s:channels[ch_info(a:channel).id] + call OmniSharp#stdio#HandleResponse(job, message) endif endfunction @@ -106,6 +115,9 @@ function! OmniSharp#proc#vimJobStart(command) abort call OmniSharp#util#EchoErr('Not using Vim 8.0+') return -1 endif + + + call s:debug('Using vim job_start to start the following command:') call s:debug(a:command) let opts = {'err_cb': 'OmniSharp#proc#vimErrHandler'} @@ -116,8 +128,10 @@ function! OmniSharp#proc#vimJobStart(command) abort \ 'start_time': reltime(), \ 'job_id': job_start(a:command, opts) \} - let channel_id = job_getchannel(job.job_id) + let job.pid = job_info(job.job_id).process + let channel_id = ch_info(job_getchannel(job.job_id)).id let s:channels[channel_id] = job + call OmniSharp#log#Log(job, join(split(execute('version'), "\n")[0:1], ', ')) return job endfunction @@ -177,7 +191,7 @@ function! OmniSharp#proc#Start(command, jobkey) abort if job_status(job.job_id) ==# 'run' let s:jobs[a:jobkey] = job else - call OmniSharp#util#EchoErr('Could not run command: ' . join(a:command, ' ')) + call OmniSharp#util#EchoErr('Could not run command: ' . join(a:command)) endif elseif OmniSharp#proc#supportsVimDispatch() let job = OmniSharp#proc#dispatchStart(a:command) @@ -186,11 +200,20 @@ function! OmniSharp#proc#Start(command, jobkey) abort let job = OmniSharp#proc#vimprocStart(a:command) let s:jobs[a:jobkey] = job else - call OmniSharp#util#EchoErr('Please use neovim, or vim 8.0+ or install either vim-dispatch or vimproc.vim plugin to use this feature') + call OmniSharp#util#EchoErr( + \ 'Please use neovim, or vim 8.0+ or install either vim-dispatch or ' . + \ 'vimproc.vim plugin to use this feature') endif if type(job) == type({}) let job.sln_or_dir = a:jobkey let job.loaded = 0 + call OmniSharp#log#Log(job, '') + call OmniSharp#log#Log(job, 'OmniSharp server started.') + call OmniSharp#log#Log(job, ' Path: ' . OmniSharp#util#GetServerPath()) + call OmniSharp#log#Log(job, ' Target: ' . a:jobkey) + call OmniSharp#log#Log(job, ' PID: ' . job.pid) + call OmniSharp#log#Log(job, ' Command: ' . join(a:command), 1) + call OmniSharp#log#Log(job, '') silent doautocmd User OmniSharpStarted endif return job @@ -211,9 +234,7 @@ function! OmniSharp#proc#StopJob(jobkey) abort elseif OmniSharp#proc#supportsVimProc() call job.kill() endif - if has_key(s:jobs, a:jobkey) - call remove(s:jobs, a:jobkey) - endif + let job.stopped = 1 silent doautocmd User OmniSharpStopped endfunction @@ -232,10 +253,10 @@ function! OmniSharp#proc#IsJobRunning(jobkey) abort let job = get(s:jobs, a:jobkey) endif if OmniSharp#proc#supportsNeovimJobs() - return 1 + return !get(job, 'stopped', 0) elseif OmniSharp#proc#supportsVimJobs() let status = job_status(job.job_id) - return status ==# 'run' + return status ==# 'run' && !get(job, 'stopped', 0) elseif OmniSharp#proc#supportsVimDispatch() return dispatch#completed(job) elseif OmniSharp#proc#supportsVimProc() diff --git a/autoload/OmniSharp/project.vim b/autoload/OmniSharp/project.vim new file mode 100644 index 000000000..a3f8487dc --- /dev/null +++ b/autoload/OmniSharp/project.vim @@ -0,0 +1,100 @@ +let s:save_cpo = &cpoptions +set cpoptions&vim + +function! OmniSharp#project#CountLoaded() abort + if !g:OmniSharp_server_stdio | return 0 | endif + let host = OmniSharp#GetHost() + return get(OmniSharp#proc#GetJob(host.sln_or_dir), 'projects_loaded', 0) +endfunction + +function! OmniSharp#project#CountTotal() abort + if !g:OmniSharp_server_stdio | return 0 | endif + let host = OmniSharp#GetHost() + return get(OmniSharp#proc#GetJob(host.sln_or_dir), 'projects_total', 0) +endfunction + +function! OmniSharp#project#RegisterLoaded(job) abort + if a:job.loaded | return | endif + if g:OmniSharp_server_display_loading + let elapsed = reltimefloat(reltime(a:job.start_time)) + echomsg printf('Loaded server for %s in %.1fs', + \ a:job.sln_or_dir, elapsed) + endif + let a:job.loaded = 1 + silent doautocmd User OmniSharpReady + call OmniSharp#log#Log(a:job, 'All projects loaded') + " If any requests are waiting to be replayed after the server is loaded, + " replay them now. + " + " TODO: If we start listening for individual project load status, then do + " this when this project finishes loading, instead of when the entire + " solution finishes loading. + " + " Remove this 1s delay if/when we get better project-laoded information + " - currently we don't get any better indicators from the server. + call timer_start(1000, function('OmniSharp#stdio#ReplayOnLoad', [a:job])) +endfunction + +" Listen for stdio server-loaded events +function! OmniSharp#project#ParseEvent(job, event, eventBody) abort + " Full load: Wait for all projects to load before marking server as ready + let projects_loaded = get(a:job, 'projects_loaded', 0) + let projects_total = get(a:job, 'projects_total', 0) + if a:job.loaded && projects_loaded == projects_total | return | endif + + if !has_key(a:job, 'loading_timeout') + " Create a timeout to mark a job as loaded after 180 seconds despite not + " receiving the expected server events. + let a:job.loading_timeout = timer_start( + \ g:OmniSharp_server_loading_timeout * 1000, + \ function('s:ServerLoadTimeout', [a:job])) + endif + if !has_key(a:job, 'loading') + let a:job.loading = [] + endif + let name = get(a:eventBody, 'Name', '') + let message = get(a:eventBody, 'Message', '') + if a:event ==# 'started' + call OmniSharp#actions#workspace#Get(a:job) + elseif name ==# 'OmniSharp.MSBuild.ProjectManager' + let project = matchstr(message, '''\zs.*\ze''') + if message =~# '^Queue project' + call add(a:job.loading, project) + if len(a:job.loading) > projects_total + let a:job.projects_total = len(a:job.loading) + silent doautocmd User OmniSharpProjectUpdated + endif + endif + if message =~# '^Successfully loaded project' + \ || message =~# '^Failed to load project' + if message[0] ==# 'F' + echom 'Failed to load project: ' . project + endif + call filter(a:job.loading, {idx,val -> val !=# project}) + let a:job.projects_loaded = projects_loaded + 1 + silent doautocmd User OmniSharpProjectUpdated + if len(a:job.loading) == 0 + call OmniSharp#project#RegisterLoaded(a:job) + unlet a:job.loading + call timer_stop(a:job.loading_timeout) + unlet a:job.loading_timeout + endif + endif + endif +endfunction + +function! s:ServerLoadTimeout(job, timer) abort + if g:OmniSharp_server_display_loading + echomsg printf('Server load notification for %s not received after %d seconds - continuing.', + \ a:job.sln_or_dir, g:OmniSharp_server_loading_timeout) + endif + let a:job.loaded = 1 + unlet a:job.loading + unlet a:job.loading_timeout + silent doautocmd User OmniSharpReady +endfunction + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim:et:sw=2:sts=2 diff --git a/autoload/OmniSharp/py.vim b/autoload/OmniSharp/py.vim index 450fae6fb..f2f50cfb3 100644 --- a/autoload/OmniSharp/py.vim +++ b/autoload/OmniSharp/py.vim @@ -8,7 +8,7 @@ set cpoptions&vim let s:alive_cache = get(s:, 'alive_cache', []) let s:pycmd = has('python3') ? 'python3' : 'python' let s:pyfile = has('python3') ? 'py3file' : 'pyfile' - +let g:OmniSharp_py_err = {} let g:OmniSharp_python_path = OmniSharp#util#PathJoin(['python']) " Default map of solution files and directories to ports. diff --git a/autoload/OmniSharp/stdio.vim b/autoload/OmniSharp/stdio.vim index e23bf0c91..f6162d941 100644 --- a/autoload/OmniSharp/stdio.vim +++ b/autoload/OmniSharp/stdio.vim @@ -3,18 +3,31 @@ set cpoptions&vim let s:nextseq = get(s:, 'nextseq', 1001) let s:requests = get(s:, 'requests', {}) -let s:pendingRequests = get(s:, 'pendingRequests', {}) function! OmniSharp#stdio#HandleResponse(job, message) abort try let res = json_decode(a:message) catch - call OmniSharp#log#Log(a:job.job_id . ' ' . a:message, 'info') - call OmniSharp#log#Log(a:job.job_id . ' JSON error: ' . v:exception, 'info') + let a:job.json_errors = get(a:job, 'json_errors', 0) + 1 + if !OmniSharp#proc#IsJobRunning(a:job) + return + endif + if a:job.json_errors >= 3 && !a:job.loaded + call OmniSharp#log#Log(a:job, '3 errors caught while loading: stopping') + call OmniSharp#proc#StopJob(a:job.sln_or_dir) + echohl WarningMsg + echomsg 'You appear to be running an HTTP server in stdio mode - ' . + \ 'upgrade to the stdio server with :OmniSharpInstall, or to continue ' . + \' in HTTP mode add the following to your .vimrc and restart Vim: ' + \ 'let g:OmniSharp_server_stdio = 0' + echohl None + return + endif + call OmniSharp#log#Log(a:job, a:message) + call OmniSharp#log#Log(a:job, 'JSON error: ' . v:exception) return endtry - let loglevel = get(res, 'Event', '') ==? 'log' ? 'info' : 'debug' - call OmniSharp#log#Log(a:job.job_id . ' ' . a:message, loglevel) + call OmniSharp#log#LogServer(a:job, a:message, res) if get(res, 'Type', '') ==# 'event' call s:HandleServerEvent(a:job, res) return @@ -33,111 +46,52 @@ function! OmniSharp#stdio#HandleResponse(job, message) abort endfunction function! s:HandleServerEvent(job, res) abort - if has_key(a:res, 'Body') && type(a:res.Body) == type({}) - if !a:job.loaded - - " Listen for server-loaded events - "------------------------------------------------------------------------- - if g:OmniSharp_server_stdio_quickload - " Quick load: Mark server as loaded as soon as configuration is finished - let message = get(a:res.Body, 'Message', '') - if message ==# 'Configuration finished.' - let a:job.loaded = 1 - silent doautocmd User OmniSharpReady - call s:ReplayRequests() - endif - else - " Complete load: Wait for all projects to be loaded before marking - " server as loaded - if !has_key(a:job, 'loading_timeout') - " Create a timeout to mark a job as loaded after 30 seconds despite - " not receiving the expected server events. - let a:job.loading_timeout = timer_start( - \ g:OmniSharp_server_loading_timeout * 1000, - \ function('s:ServerLoadTimeout', [a:job])) - endif - if !has_key(a:job, 'loading') - let a:job.loading = [] - endif - let name = get(a:res.Body, 'Name', '') - let message = get(a:res.Body, 'Message', '') - if name ==# 'OmniSharp.MSBuild.ProjectManager' - let project = matchstr(message, '''\zs.*\ze''') - if message =~# '^Queue project' - call add(a:job.loading, project) - endif - if message =~# '^Successfully loaded project' - \ || message =~# '^Failed to load project' - if message[0] ==# 'F' - echom 'Failed to load project: ' . project - endif - call filter(a:job.loading, {idx,val -> val !=# project}) - if len(a:job.loading) == 0 - if g:OmniSharp_server_display_loading - let elapsed = reltimefloat(reltime(a:job.start_time)) - echomsg printf('Loaded server for %s in %.1fs', - \ a:job.sln_or_dir, elapsed) - endif - let a:job.loaded = 1 - silent doautocmd User OmniSharpReady - - " TODO: Remove this delay once we have better information about - " when the server is completely initialised: - " https://github.com/OmniSharp/omnisharp-roslyn/issues/1521 - call timer_start(1000, function('s:ReplayRequests')) - " call s:ReplayRequests() - - unlet a:job.loading - call timer_stop(a:job.loading_timeout) - unlet a:job.loading_timeout - endif - endif - endif - endif + let body = get(a:res, 'Body', 0) + if type(body) != type({}) + let body = {} + endif - else + " Handle any project loading events + call OmniSharp#project#ParseEvent(a:job, get(a:res, 'Event', ''), body) - " Server is loaded, listen for diagnostics - "------------------------------------------------------------------------- + if !empty(body) + + " Listen for diagnostics. + " When OmniSharp-roslyn is configured with `EnableAnalyzersSupport`, the + " first diagnostic or code action request will trigger analysis of ALL + " solution documents. + " These diagnostics results are sent out over stdio so we might as well + " capture them and update ALE for loaded buffers. + if has_key(g:, 'OmniSharp_ale_diagnostics_requested') if get(a:res, 'Event', '') ==# 'Diagnostic' - if has_key(g:, 'OmniSharp_ale_diagnostics_requested') - for result in get(a:res.Body, 'Results', []) - let fname = OmniSharp#util#TranslatePathForClient(result.FileName) - let bufinfo = getbufinfo(fname) - if len(bufinfo) == 0 || !has_key(bufinfo[0], 'bufnr') - continue - endif - let bufnr = bufinfo[0].bufnr - call ale#other_source#StartChecking(bufnr, 'OmniSharp') - let opts = { 'BufNum': bufnr } - let quickfixes = OmniSharp#locations#Parse(result.QuickFixes) - call ale#sources#OmniSharp#ProcessResults(opts, quickfixes) - endfor - endif - elseif get(a:res, 'Event', '') ==# 'TestMessage' - " Diagnostics received while running tests - let lines = split(a:res.Body.Message, '\n') - for line in lines - if get(a:res.Body, 'MessageLevel', '') ==# 'error' - echohl WarningMsg | echomsg line | echohl None - elseif g:OmniSharp_runtests_echo_output - echomsg line + for result in get(body, 'Results', []) + let fname = OmniSharp#util#TranslatePathForClient(result.FileName) + let bufinfo = getbufinfo(fname) + if len(bufinfo) == 0 || !has_key(bufinfo[0], 'bufnr') + continue endif + let bufnr = bufinfo[0].bufnr + call ale#other_source#StartChecking(bufnr, 'OmniSharp') + let opts = { 'BufNum': bufnr } + let quickfixes = OmniSharp#locations#Parse(result.QuickFixes) + call ale#sources#OmniSharp#ProcessResults(opts, quickfixes) endfor endif + endif + " Diagnostics received while running tests + if get(a:res, 'Event', '') ==# 'TestMessage' + let lines = split(body.Message, '\n') + for line in lines + if get(body, 'MessageLevel', '') ==# 'error' + echohl WarningMsg | echomsg line | echohl None + elseif g:OmniSharp_runtests_echo_output + echomsg line + endif + endfor endif - endif -endfunction -function! s:ServerLoadTimeout(job, timer) abort - if g:OmniSharp_server_display_loading - echomsg printf('Server load notification for %s not received after %d seconds - continuing.', - \ a:job.sln_or_dir, g:OmniSharp_server_loading_timeout) endif - let a:job.loaded = 1 - unlet a:job.loading - unlet a:job.loading_timeout endfunction function! OmniSharp#stdio#Request(command, opts) abort @@ -145,13 +99,37 @@ function! OmniSharp#stdio#Request(command, opts) abort let [bufnr, lnum, cnum] = s:lastPosition elseif has_key(a:opts, 'BufNum') && a:opts.BufNum != bufnr('%') let bufnr = a:opts.BufNum - let lnum = 1 - let cnum = 1 + let lnum = get(a:opts, 'LineNum', 1) + let cnum = get(a:opts, 'ColNum', 1) else let bufnr = bufnr('%') - let lnum = line('.') - let cnum = col('.') + let lnum = get(a:opts, 'LineNum', line('.')) + let cnum = get(a:opts, 'ColNum', col('.')) + endif + let host = OmniSharp#GetHost(bufnr) + let job = host.job + if !OmniSharp#proc#IsJobRunning(job) + return 0 + endif + + if has_key(a:opts, 'Initializing') + " The buffer is being initialized - this request will always be sent + else + if !get(host, 'initialized') + " Replay the request when the buffer has been initialized with the server + let opts = extend(a:opts, { + \ 'BufNum': bufnr, + \ 'LineNum': lnum, + \ 'ColNum': cnum + \}) + if has_key(opts, 'UsePreviousPosition') + unlet opts.UsePreviousPosition + endif + call OmniSharp#buffer#Initialize(job, bufnr, a:command, opts) + return 0 + endif endif + if has_key(a:opts, 'SavePosition') let s:lastPosition = [bufnr, lnum, cnum] endif @@ -185,26 +163,26 @@ function! OmniSharp#stdio#Request(command, opts) abort \ 'Column': cnum, \ } \} - if send_buffer let body.Arguments.Buffer = buffer endif - return OmniSharp#stdio#RequestSend(body, a:command, a:opts, sep) -endfunction -function! OmniSharp#stdio#RequestSend(body, command, opts, ...) abort - let sep = a:0 ? a:1 : '' + call s:Request(job, body, a:command, a:opts, sep) - let job = OmniSharp#GetHost().job - if type(job) != type({}) || !has_key(job, 'job_id') || !job.loaded - if has_key(a:opts, 'ReplayOnLoad') && !has_key(s:pendingRequests, a:command) - " This request should be replayed when the server is fully loaded - let s:pendingRequests[a:command] = a:opts - endif - return 0 + if has_key(a:opts, 'ReplayOnLoad') + let replay_opts = filter(copy(a:opts), 'v:key !=# "ReplayOnLoad"') + call s:QueueForReplayOnLoad(job, bufnr, a:command, replay_opts) endif - let job_id = job.job_id - call OmniSharp#log#Log(job_id . ' Request: ' . a:command, 'debug') + + return 1 +endfunction + +function! OmniSharp#stdio#RequestGlobal(job, command, opts) abort + call s:Request(a:job, {}, a:command, a:opts) +endfunction + +function! s:Request(job, body, command, opts, ...) abort + call OmniSharp#log#Log(a:job, 'Request: ' . a:command, 1) let a:body['Command'] = a:command let a:body['Seq'] = s:nextseq @@ -212,6 +190,7 @@ function! OmniSharp#stdio#RequestSend(body, command, opts, ...) abort if has_key(a:opts, 'Parameters') call extend(a:body.Arguments, a:opts.Parameters, 'force') endif + let sep = a:0 ? a:1 : '' if sep !=# '' let encodedBody = substitute(json_encode(a:body), sep, '\\r\\n', 'g') else @@ -223,19 +202,37 @@ function! OmniSharp#stdio#RequestSend(body, command, opts, ...) abort let s:requests[s:nextseq].ResponseHandler = a:opts.ResponseHandler endif let s:nextseq += 1 - call OmniSharp#log#Log(encodedBody, 'debug') + if get(g:, 'OmniSharp_proc_debug') + " The raw request is already logged by the server in debug mode. + call OmniSharp#log#Log(a:job, encodedBody, 1) + endif if has('nvim') - call chansend(job_id, encodedBody . "\n") + call chansend(a:job.job_id, encodedBody . "\n") else - call ch_sendraw(job_id, encodedBody . "\n") + call ch_sendraw(a:job.job_id, encodedBody . "\n") + endif +endfunction + +function! s:QueueForReplayOnLoad(job, bufnr, command, opts) abort + if type(a:job) == type({}) && !get(a:job, 'loaded') + " The project is still loading - it is possible to highlight but those + " highlights will be improved once loading is complete, so listen for that + " and re-run the highlighting on project load. + let pending = get(a:job, 'pending_load_requests', {}) + let pending[a:bufnr] = get(pending, a:bufnr, {}) + let pending[a:bufnr][a:command] = a:opts + let a:job.pending_load_requests = pending endif - return 1 endfunction -function! s:ReplayRequests(...) abort - for key in keys(s:pendingRequests) - call OmniSharp#stdio#Request(key, s:pendingRequests[key]) - unlet s:pendingRequests[key] +function! OmniSharp#stdio#ReplayOnLoad(job, ...) abort + call OmniSharp#log#Log(a:job, 'Replaying on-load requests') + for bufnr in keys(get(a:job, 'pending_load_requests', {})) + for key in keys(a:job.pending_load_requests[bufnr]) + call OmniSharp#stdio#Request(key, a:job.pending_load_requests[bufnr][key]) + unlet a:job.pending_load_requests[bufnr][key] + endfor + unlet a:job.pending_load_requests[bufnr] endfor endfunction diff --git a/autoload/OmniSharp/util.vim b/autoload/OmniSharp/util.vim index e91688bd7..4674e153a 100644 --- a/autoload/OmniSharp/util.vim +++ b/autoload/OmniSharp/util.vim @@ -136,6 +136,10 @@ function! OmniSharp#util#EchoErr(msg) echohl ErrorMsg | echomsg a:msg | echohl None endfunction +function! OmniSharp#util#GetServerPath() abort + return get(s:, 'server_path', '') +endfunction + function! OmniSharp#util#GetStartCmd(solution_file) abort let solution_path = a:solution_file if fnamemodify(solution_path, ':t') ==? s:roslyn_server_files @@ -170,8 +174,8 @@ function! OmniSharp#util#GetStartCmd(solution_file) abort endif let command += ['-s', solution_path] - if g:OmniSharp_loglevel ==? 'debug' - let command += ['-v'] + if g:OmniSharp_loglevel !=? 'info' + let command += ['-l', g:OmniSharp_loglevel] endif if !has('win32') && !s:is_cygwin() && g:OmniSharp_server_use_mono diff --git a/autoload/ale/sources/OmniSharp.vim b/autoload/ale/sources/OmniSharp.vim index f928ecee6..e4984c833 100644 --- a/autoload/ale/sources/OmniSharp.vim +++ b/autoload/ale/sources/OmniSharp.vim @@ -4,7 +4,7 @@ function! ale#sources#OmniSharp#WantResults(buffer) abort call ale#other_source#StartChecking(a:buffer, 'OmniSharp') let opts = { 'BufNum': a:buffer } let Callback = function('ale#sources#OmniSharp#ProcessResults', [opts]) - call OmniSharp#actions#diagnostics#StdioCheck(opts, Callback) + call OmniSharp#actions#diagnostics#StdioCheck(a:buffer, Callback) endfunction function! ale#sources#OmniSharp#ProcessResults(opts, locations) abort diff --git a/doc/omnisharp-vim.txt b/doc/omnisharp-vim.txt index 942dbdbf8..45518e659 100644 --- a/doc/omnisharp-vim.txt +++ b/doc/omnisharp-vim.txt @@ -31,7 +31,6 @@ CONTENTS *omnisharp-contents 1. DEPENDENCIES *omnisharp-dependencies* Required:~ - - Mono OR .NET Framework - Either: - Vim 8.0 or neovim or @@ -59,7 +58,8 @@ Use the stdio version of OmniSharp-roslyn. When the stdio version is used, OmniSharp-vim interacts asynchronously with it, with no other dependencies. The alternative is the HTTP version of the server, which has synchronous communication. -Default: 0 > +Note: changes require server reinstall: |:OmniSharpInstall| +Default: 1 > let g:OmniSharp_server_stdio = 1 < *'g:OmniSharp_server_install'* @@ -84,6 +84,7 @@ Default: None > The roslyn server must be run with mono on Linux and OSX. The roslyn server binaries usually come with an embedded mono, but this can be overridden to use the installed mono with this option. Note that mono must be in the $PATH. +Note: changes require server reinstall: |:OmniSharpInstall| Default: 0 > let g:OmniSharp_server_use_mono = 1 < @@ -93,8 +94,8 @@ opening a `*.cs` file. Default: 1 > let g:OmniSharp_start_server = 1 < *'g:OmniSharp_timeout'* -Use this option to specify the time (in seconds) to wait for a response from -the server. +Use this option to specify the time (in seconds) to wait for a synchronous +response from the server. Default: 1 > let g:OmniSharp_timeout = 1 < @@ -130,18 +131,10 @@ Default: 0 < *'g:OmniSharp_server_display_loading'* Stdio only -Echo "Server loaded for ...sln" message when the server is ready. +Echo "Server loaded for ...sln" message when the server has loaded all +projects. Default: 1 > let g:OmniSharp_server_display_loading = 1 -< - *'g:OmniSharp_server_loading_timeout'* - Stdio only -When the server is started, OmniSharp-vim listens for notifications from the -server to know when the server is ready for requests. However, these -notifications may not be correctly sent, in which case the server can be -considered "loaded" after a timeout period. -Default: 30 (seconds) > - let g:OmniSharp_server_loading_timeout = 30 < *'g:OmniSharp_host'* HTTP only @@ -210,7 +203,8 @@ Example: > let g:OmniSharp_diagnostic_exclude_paths = [ \ 'obj\\', \ '[Tt]emp\\', - \ '\.nuget\\' + \ '\.nuget\\', + \ '\' \] < ------------------------------------------------------------------------------- @@ -218,7 +212,9 @@ Example: > *'g:OmniSharp_highlighting'* Enable automatic semantic highlighting. -Default: 0 > +Default: 2 > + " Disable automatic highlighting + let g:OmniSharp_highlighting = 0 " Highlight on BufEnter let g:OmniSharp_highlighting = 1 " Highlight on BufEnter, InsertLeave and TextChanged @@ -389,8 +385,8 @@ Default: 0 > Use this option to specify whether OmniSharp will fetch full documentation on completion suggestions. By default, only type/method signatures are fetched for performance reasons. Full documentation can still be fetched when needed using -the |:OmniSharpDocumentation| command. Default: 0 > - let g:omnicomplete_fetch_full_documentation = 0 +the |:OmniSharpDocumentation| command. Default: 1 > + let g:omnicomplete_fetch_full_documentation = 1 < =============================================================================== 4. COMMANDS *omnisharp-commands* @@ -590,7 +586,14 @@ customise your environment depending on the server status. *OmniSharpStarted* OmniSharpStarted Fired as soon as the server is started - it will not immediately be - responsive to commands + responsive to commands. + + *OmniSharpProjectUpdated* +OmniSharpProjectUpdate + Fired when the project status changes while OmniSharp-roslyn is starting. + Used to e.g. display the total number of projects + (|OmniSharp#project#CountTotal()|), and number of loaded projects + (|OmniSharp#project#CountLoaded()|) in the statusline. *OmniSharpReady* OmniSharpReady diff --git a/ftdetect/omnisharplog.vim b/ftdetect/omnisharplog.vim new file mode 100644 index 000000000..7da569a65 --- /dev/null +++ b/ftdetect/omnisharplog.vim @@ -0,0 +1 @@ +autocmd BufRead,BufNewFile *omnisharp.log set filetype=omnisharplog diff --git a/ftplugin/omnisharplog/OmniSharp.vim b/ftplugin/omnisharplog/OmniSharp.vim new file mode 100644 index 000000000..285f0702d --- /dev/null +++ b/ftplugin/omnisharplog/OmniSharp.vim @@ -0,0 +1,2 @@ +set foldlevel=0 +set foldmethod=syntax diff --git a/plugin/OmniSharp.vim b/plugin/OmniSharp.vim index 13f4180c5..2fbb736e9 100644 --- a/plugin/OmniSharp.vim +++ b/plugin/OmniSharp.vim @@ -6,14 +6,10 @@ let g:OmniSharp_temp_dir = get(g:, 'OmniSharp_temp_dir', fnamemodify(tempname(), let g:OmniSharp_lookup_metadata = get(g:, 'OmniSharp_lookup_metadata', 1) -let g:OmniSharp_server_stdio = get(g:, 'OmniSharp_server_stdio', 0) +let g:OmniSharp_server_stdio = get(g:, 'OmniSharp_server_stdio', 1) -" When g:OmniSharp_server_stdio_quickload = 1, consider server 'loaded' once -" 'Configuration finished' is received. When this is 0, wait for notification -" that all projects have been loaded. -let g:OmniSharp_server_stdio_quickload = get(g:, 'OmniSharp_server_stdio_quickload', 0) let g:OmniSharp_server_display_loading = get(g:, 'OmniSharp_server_display_loading', 1) -let g:OmniSharp_server_loading_timeout = get(g:, 'OmniSharp_server_loading_timeout', 20) +let g:OmniSharp_server_loading_timeout = get(g:, 'OmniSharp_server_loading_timeout', 180) " Use mono to start the roslyn server on *nix let g:OmniSharp_server_use_mono = get(g:, 'OmniSharp_server_use_mono', 0) @@ -67,14 +63,14 @@ let g:OmniSharp_runtests_echo_output = get(g:, 'OmniSharp_runtests_echo_output', " Set to 1 when ultisnips is installed let g:OmniSharp_want_snippet = get(g:, 'OmniSharp_want_snippet', 0) -let g:omnicomplete_fetch_full_documentation = get(g:, 'omnicomplete_fetch_full_documentation', 0) +let g:omnicomplete_fetch_full_documentation = get(g:, 'omnicomplete_fetch_full_documentation', 1) command! -bar -nargs=? OmniSharpInstall call OmniSharp#Install() command! -bar -nargs=? OmniSharpOpenLog call OmniSharp#log#Open() " Initialise automatic type and interface highlighting " Preserve backwards compatibility with older version g:OmniSharp_highlight_types -let g:OmniSharp_highlighting = get(g:, 'OmniSharp_highlighting', get(g:, 'OmniSharp_highlight_types', 0)) +let g:OmniSharp_highlighting = get(g:, 'OmniSharp_highlighting', get(g:, 'OmniSharp_highlight_types', 2)) if g:OmniSharp_highlighting augroup OmniSharp_HighlightTypes autocmd! diff --git a/syntax/omnisharplog.vim b/syntax/omnisharplog.vim new file mode 100644 index 000000000..d95379bca --- /dev/null +++ b/syntax/omnisharplog.vim @@ -0,0 +1,40 @@ +if exists('b:current_syntax') + finish +endif + +let s:save_cpo = &cpoptions +set cpoptions&vim + +syn match oslName "^\[\w\{4,5\}\]: .*$"hs=s+7 contains=oslTrace,oslDebug,oslInformation,oslWarning,oslError,oslCritical + +syn match oslTrace "^\[trce\]"ms=s+1,me=e-1 contained +syn match oslDebug "^\[dbug\]"ms=s+1,me=e-1 contained +syn match oslInformation "^\[info\]"ms=s+1,me=e-1 contained +syn match oslWarning "^\[warn\]"ms=s+1,me=e-1 contained +syn match oslError "^\[fail\]"ms=s+1,me=e-1 contained +syn match oslError "^\[ERROR\]"ms=s+1,me=e-1 contained +syn match oslCritical "^\[crit\]"ms=s+1,me=e-1 contained + +syn match oslEndpoint "^Request: .*$"hs=s+9 +syn match oslServerEndpoint "^Server \%(Request\|Response\): .*$"hs=s+16 + +syn region oslRequestResponse start="\*\{12\}\s\+\%(Request\|Response\)\s\+\*\{12\}" end="^}" transparent fold + +hi def link oslName Comment + +hi def link oslTrace Identifier +hi def link oslDebug NonText +hi def link oslInformation Type +hi def link oslWarning WarningMsg +hi def link oslError ErrorMsg +hi def link oslCritical ErrorMsg + +hi def link oslEndpoint Identifier +hi def link oslServerEndpoint Constant + +let b:current_syntax = 'omnisharplog' + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim:et:sw=2:sts=2