Make the IDocumentNavigationSerivice entirely async.#59784
Make the IDocumentNavigationSerivice entirely async.#59784CyrusNajmabadi merged 13 commits intodotnet:mainfrom
Conversation
| @@ -58,63 +64,65 @@ public void AugmentPeekSession(IPeekSession session, IList<IPeekableItem> peekab | |||
|
|
|||
| _uiThreadOperationExecutor.Execute(EditorFeaturesResources.Peek, EditorFeaturesResources.Loading_Peek_information, allowCancellation: true, showProgress: false, action: context => | |||
There was a problem hiding this comment.
review with whitespace off.
| Service = service; | ||
| } | ||
|
|
||
| public IWorkspaceThreadingService Service { get; } |
There was a problem hiding this comment.
@jasonmalinowski i needed this for teh DocumentNavigationOperation change below. That's a public API we can't pass an IThreadingContext into. INstead, it just gets a WOrkspace, so we need a way to get this from that.
There was a problem hiding this comment.
Yeah, this seems reasonable in the sense that I don't have a good alternative here.
| { | ||
| var navigationService = workspace.Services.GetService<IDocumentNavigationService>(); | ||
| navigationService.TryNavigateToPosition(workspace, _documentId, _position, cancellationToken); | ||
| var threadingService = workspace.Services.GetService<IWorkspaceThreadingServiceProvider>(); |
There was a problem hiding this comment.
this is a new concept @jasonmalinowski . not sure if there's any better way here.
There was a problem hiding this comment.
Yeah, I don't have many better options other than leave the sync method on the IDocumentNavigationService and then inline this, which of course kinda undoes what you're trying to do. So unless @sharwell has a better idea, I'm OK with this in the "I will pinch my nose and sign off because I don't have a better idea".
| internal interface IWorkspaceThreadingServiceProvider : IWorkspaceService | ||
| { | ||
| IWorkspaceThreadingService Service { get; } | ||
| } |
There was a problem hiding this comment.
moral equivalent of IWorkspaceThreadingService @jasonmalinowski . However the above one is only available through MEF, where i need it when i only have a workspace available.
src/Tools/ExternalAccess/FSharp/Navigation/FSharpDocumentNavigationService.cs
Outdated
Show resolved
Hide resolved
jasonmalinowski
left a comment
There was a problem hiding this comment.
Some comments but generally seems fine. I don't like the hack you had to do that you flagged but I don't have a better idea.
src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/Navigation/IDocumentNavigationService.cs
Outdated
Show resolved
Hide resolved
| /// </summary> | ||
| /// <remarks>Only legal to call on the UI thread.</remarks> | ||
| bool CanNavigateToLineAndOffset(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken); | ||
| Task<bool> CanNavigateToLineAndOffsetAsync(Workspace workspace, DocumentId documentId, int lineNumber, int offset, CancellationToken cancellationToken); |
There was a problem hiding this comment.
It's a bit fishy to me we would have added the async methods but left the sync ones too -- was there some reason that still applies? Is this being used via IVT or something?
src/Tools/ExternalAccess/FSharp/Navigation/IFSharpDocumentNavigationService.cs
Outdated
Show resolved
Hide resolved
| { | ||
| var navigationService = workspace.Services.GetService<IDocumentNavigationService>(); | ||
| navigationService.TryNavigateToPosition(workspace, _documentId, _position, cancellationToken); | ||
| var threadingService = workspace.Services.GetService<IWorkspaceThreadingServiceProvider>(); |
There was a problem hiding this comment.
Yeah, I don't have many better options other than leave the sync method on the IDocumentNavigationService and then inline this, which of course kinda undoes what you're trying to do. So unless @sharwell has a better idea, I'm OK with this in the "I will pinch my nose and sign off because I don't have a better idea".
| Me.ProvidedLineNumber = lineNumber | ||
|
|
||
| Return CanNavigateToLineAndOffsetReturnValue | ||
| Return If(CanNavigateToLineAndOffsetReturnValue, SpecializedTasks.True, SpecializedTasks.False) |
There was a problem hiding this comment.
We don't have a helper for this?
There was a problem hiding this comment.
couldn't find one :)
...ore/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentNavigationServiceWrapper.cs
Show resolved
Hide resolved
src/EditorFeatures/Core/Implementation/InlineRename/InlineRenameService.cs
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| var updatedSolution = operations.OfType<ApplyChangesOperation>().FirstOrDefault()?.ChangedSolution ?? oldSolution; | ||
| TryNavigateToLocationOrStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken); |
There was a problem hiding this comment.
Wait, so if applied is false we do the navigation anyways?
| // Mappings for opened razor files are retrieved via the LSP client making a request to the razor server. | ||
| // If we wait for the result on the UI thread, we will hit a bug in the LSP client that brings us to a code path | ||
| // using ConfigureAwait(true). This deadlocks as it then attempts to return to the UI thread which is already blocked by us. | ||
| // Instead, we invoke this in JTF run which will mitigate deadlocks when the ConfigureAwait(true) | ||
| // tries to switch back to the main thread in the LSP client. | ||
| // Link to LSP client bug for ConfigureAwait(true) - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1216657 |
There was a problem hiding this comment.
Is this comment applicable anymore?
There was a problem hiding this comment.
oh that's terrifying.
There was a problem hiding this comment.
this should no longer apply as we're either all async, or our callers need to be in JTF run calls themselves (this PR).
Extracting out from #59759.
This is step one in making navigation saner. Next step is to make the service non-imperative itself. e.g. it can be queried to determine navigatino locations independent of actually navigating to those locations.
In many cases, this asynchrony just pushed up till it reached something already async (yaay).
In other cases this async pushed up until we got to a synchronous location due to an API we implement, but that had an ITHreadingContext. In those cases we can jsut JTF run the async code then.
In the final cases this pushed up to a synchronous location without an IThreadingContext. In those cases, an IThreadingContext was imported and passed in to support this.