-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refresh diagnostics on project changes #10964
Conversation
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see now this is how the semantic tokens refresher works, so I'm going to approve, but I think there is an argument to be made to move both impls to the AsyncBatchingWorkQueue as its a bit more battle hardened. Could even do a common base class for both that does most of the work, and just has an abstract method to check client capabilites, and a property for which LSP method to send.
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/WorkspaceDiagnosticRefreshTest.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
private bool? _supportsRefresh; | ||
private bool _waitingToRefresh; | ||
private Task _refreshTask = Task.CompletedTask; | ||
private readonly AsyncBatchingWorkQueue _queue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amusingly, I had used an AsyncBatchingWorkQueue in #10140 and removed it based on feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well I'm bringing it back! :P
...azor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceSemanticTokensRefreshNotifier.cs
Outdated
Show resolved
Hide resolved
...azor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceSemanticTokensRefreshNotifier.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/WorkspaceDiagnosticsRefresher.cs
Outdated
Show resolved
Hide resolved
var clientConnection = new StrictMock<IClientConnection>(); | ||
clientConnection | ||
.Setup(c => c.SendNotificationAsync(Methods.WorkspaceDiagnosticRefreshName, It.IsAny<CancellationToken>())) | ||
.Returns(Task.CompletedTask) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does ReturnsAsync
not work here and below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idk tbh. What's the difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like not. ReturnsAsync
doesn't have a way to do a Task
, it requires Task<T>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think I'm remembering a Moq extension I had added in a branch to address that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idk tbh. What's the difference?
ReturnsAsync(...)
is just convenient for returning Task<T>
and ValueTask<T>
in mocks.
|
||
_disposeTokenSource.Cancel(); | ||
_disposeTokenSource.Dispose(); | ||
_projectSnapshotManager.Changed -= ProjectSnapshotManager_Changed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 Consider moving this above _disposeTokenSource.Cancel()
so that no more work can be added before cancelling. If IProjectSnapshotManager.Updated
fires while the AsyncBatchingWorkQueue
is being cancelled, it might try to add work, which can result in an OperationCancelledException
. Not a huge deal, but definitely unneeded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know adding work threw if the ABWQ cancellation happened. This is good to know , thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a very tight race, but it's possible to land here:
Line 205 in 9c97001
_entireQueueCancellationToken.ThrowIfCancellationRequested(); |
return; | ||
} | ||
|
||
if ( _disposeTokenSource.IsCancellationRequested) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra space between (
and _
. Or, if you decide to take my feedback to adjust the Dispose()
method, this check isn't needed at all! 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm on a laptop today and it shows 😅
updater.CreateAndAddProject("C:/path/to/project.csproj"); | ||
}); | ||
|
||
await testAccessor.WaitForRefreshAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since publisher
is disposed above, will this ever complete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so. The underlying ABWQ method doesn't seem to check for cancellation, it just returns the task. That task is completed when this hits
|
||
public Task WaitForRefreshAsync() | ||
{ | ||
return _instance._queue.WaitUntilCurrentBatchCompletesAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, there might be a hang in tests where WaitForRefreshAsync()
is called after the WorkspaceDiagnosticRefresher
is disposed. I'm not 100% sure though. I'm trying to "psychically debug" this. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it better to check disposal here and return a Task.CompletedTask
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quick fix might be something like...
if (_instance._disposeTokenSource.IsCancellationRequested)
{
return Task.CompletedTask;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it better to check disposal here and return a
Task.CompletedTask
?
LOL. I didn't refresh quickly enough to see your comment. 🤣
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love how small the class is now.
I still think its worth considering a common base class to abstract away most of the code, but I understand if it's beyond the scope of this PR, or if I'm missing some benefit of the semantic notifier being split across two classes.
This notifies a client to refresh diagnostics when we experience project snapshot changes (except for document changed). This should help for cases where the project engine is updating but doesn't have all taghelper information yet.