From 4f2e069116ac6cd2159b55ec9f0573dda6425a1c Mon Sep 17 00:00:00 2001 From: Nick Jensen Date: Mon, 29 Jun 2020 13:11:01 +1200 Subject: [PATCH] Initialize buffers instead of full server load The OmniSharp-roslyn loading issues appear to stem from situations where multiple buffer-requests are sent before the project is fully initialized. This results in duplicate "miscellaneous" files, which are not correctly promoted to "project" files. Instead, this commit introduces a buffer "initialization" concept, where an `/updatebuffer` request is always sent as the first request for a buffer, and no further requests are sent for that buffer until a response is received (indicating that the server has registered the file, and there will be no duplicates created). --- autoload/OmniSharp.vim | 11 +++--- autoload/OmniSharp/actions/buffer.vim | 11 +++++- autoload/OmniSharp/actions/workspace.vim | 3 +- autoload/OmniSharp/buffer.vim | 26 +++++++++++++ autoload/OmniSharp/project.vim | 10 +---- autoload/OmniSharp/stdio.vim | 49 +++++++++++++----------- 6 files changed, 69 insertions(+), 41 deletions(-) diff --git a/autoload/OmniSharp.vim b/autoload/OmniSharp.vim index 2db515719..6354e5afa 100644 --- a/autoload/OmniSharp.vim +++ b/autoload/OmniSharp.vim @@ -12,21 +12,20 @@ function! OmniSharp#GetHost(...) abort let bufnr = a:0 ? a:1 : bufnr('%') if g:OmniSharp_server_stdio " Using the stdio server, b:OmniSharp_host is a dict containing the - " sln_or_dir: - " { 'sln_or_dir': '/path/to/solution_or_dir' } + " `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 { - \ 'sln_or_dir': host.sln_or_dir, - \ 'job': OmniSharp#proc#GetJob(host.sln_or_dir) - \} + 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')) 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/workspace.vim b/autoload/OmniSharp/actions/workspace.vim index 6ea9b493c..1bb592107 100644 --- a/autoload/OmniSharp/actions/workspace.vim +++ b/autoload/OmniSharp/actions/workspace.vim @@ -5,8 +5,7 @@ let s:attempts = 0 function! OmniSharp#actions#workspace#Get(job) abort let opts = { - \ 'ResponseHandler': function('s:ProjectsRH', [a:job]), - \ 'AllowUnloaded': 1 + \ 'ResponseHandler': function('s:ProjectsRH', [a:job]) \} let s:attempts += 1 call OmniSharp#stdio#RequestGlobal(a:job, '/projects', opts) 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/project.vim b/autoload/OmniSharp/project.vim index ef2abcefe..658634275 100644 --- a/autoload/OmniSharp/project.vim +++ b/autoload/OmniSharp/project.vim @@ -2,13 +2,13 @@ let s:save_cpo = &cpoptions set cpoptions&vim function! OmniSharp#project#CountLoaded() abort - " TODO: stdio guard? + 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 - " TODO: stdio guard? + 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 @@ -22,12 +22,6 @@ function! OmniSharp#project#RegisterLoaded(job) abort 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('OmniSharp#stdio#ReplayRequests', [a:job])) - " call OmniSharp#stdio#ReplayRequests(a:job) endfunction " Listen for stdio server-loaded events diff --git a/autoload/OmniSharp/stdio.vim b/autoload/OmniSharp/stdio.vim index 22bb33d36..1a3dd59aa 100644 --- a/autoload/OmniSharp/stdio.vim +++ b/autoload/OmniSharp/stdio.vim @@ -3,7 +3,6 @@ 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 @@ -100,17 +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 job = OmniSharp#GetHost(bufnr).job + let host = OmniSharp#GetHost(bufnr) + let job = host.job if !OmniSharp#proc#IsJobRunning(job) return 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 @@ -156,15 +175,6 @@ function! OmniSharp#stdio#RequestGlobal(job, command, opts) abort endfunction function! s:Request(job, body, command, opts, ...) abort - let sep = a:0 ? a:1 : '' - if !has_key(a:opts, 'AllowUnloaded') && - \ (!has_key(a:job, 'job_id') || !a: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 - endif call OmniSharp#log#Log(a:job, 'Request: ' . a:command, 1) let a:body['Command'] = a:command @@ -173,6 +183,7 @@ function! s:Request(job, 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 @@ -196,14 +207,6 @@ function! s:Request(job, body, command, opts, ...) abort return 1 endfunction -function! OmniSharp#stdio#ReplayRequests(job, ...) abort - call OmniSharp#log#Log(a:job, 'Replaying requests') - for key in keys(s:pendingRequests) - call OmniSharp#stdio#Request(key, s:pendingRequests[key]) - unlet s:pendingRequests[key] - endfor -endfunction - let &cpoptions = s:save_cpo unlet s:save_cpo