private async Task StreamingFindReferencesAsync( Document document, int caretPosition, IStreamingFindUsagesPresenter presenter) { try { // first, let's see if we even have a comment, otherwise there's no use in starting a search var relevantSymbol = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(document, caretPosition, new CancellationToken()).ConfigureAwait(false); var symbol = relevantSymbol?.symbol; if (symbol == null) { return; // would be useful if we could notify the user why we didn't do anything } // maybe using something like an info bar? var findUsagesService = document.GetLanguageService <IFindUsagesService>(); using var token = _asyncListener.BeginAsyncOperation(nameof(StreamingFindReferencesAsync)); var(context, cancellationToken) = presenter.StartSearch(EditorFeaturesResources.Find_References, supportsReferences: true); using (Logger.LogBlock( FunctionId.CommandHandler_FindAllReference, KeyValueLogMessage.Create(LogType.UserAction, m => m["type"] = "streaming"), cancellationToken)) { var symbolsToLookup = new List <ISymbol>(); foreach (var curSymbol in symbol.ContainingType.GetMembers() .Where(m => m.Kind == symbol.Kind && m.Name == symbol.Name)) { if (!document.Project.TryGetCompilation(out var compilation)) { // TODO: should we do anything more here? continue; } foreach (var sym in SymbolFinder.FindSimilarSymbols(curSymbol, compilation, cancellationToken)) { // assumption here is, that FindSimilarSymbols returns symbols inside same project var symbolsToAdd = await GatherSymbolsAsync(sym, document.Project.Solution, cancellationToken).ConfigureAwait(false); symbolsToLookup.AddRange(symbolsToAdd); } } foreach (var candidate in symbolsToLookup) { await AbstractFindUsagesService.FindSymbolReferencesAsync(context, candidate, document.Project, cancellationToken).ConfigureAwait(false); } // Note: we don't need to put this in a finally. The only time we might not hit // this is if cancellation or another error gets thrown. In the former case, // that means that a new search has started. We don't care about telling the // context it has completed. In the latter case something wrong has happened // and we don't want to run any more code in this particular context. await context.OnCompletedAsync(cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception e) when(FatalError.ReportAndCatch(e)) { } }
public async Task SearchAsync( bool searchCurrentDocument, NavigateToSearchScope scope, CancellationToken cancellationToken) { var isFullyLoaded = true; try { using var navigateToSearch = Logger.LogBlock(FunctionId.NavigateTo_Search, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken); using var asyncToken = _asyncListener.BeginAsyncOperation(GetType() + ".Search"); if (searchCurrentDocument) { await SearchCurrentDocumentAsync(cancellationToken).ConfigureAwait(false); } else { // We consider ourselves fully loaded when both the project system has completed loaded us, and we've // totally hydrated the oop side. Until that happens, we'll attempt to return cached data from languages // that support that. isFullyLoaded = await _host.IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false); await SearchAllProjectsAsync(isFullyLoaded, scope, cancellationToken).ConfigureAwait(false); } } finally { // Ensure that we actually complete all our remaining progress items so that the progress bar completes. await ProgressItemsCompletedAsync(_remainingProgressItems, cancellationToken).ConfigureAwait(false); Debug.Assert(_remainingProgressItems == 0); // Pass along isFullyLoaded so that the UI can show indication to users that results may be incomplete. _callback.Done(isFullyLoaded); } }
/// <summary> /// Starts pushing events from the given projects to the workspace hosts and notifies about open documents. /// </summary> /// <remarks>This method must be called on the foreground thread.</remarks> internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments(IEnumerable <AbstractProject> projects) { AssertIsForeground(); // If the solution is closing we shouldn't do anything, because all of our state is // in the process of going away. This can happen if we receive notification that a document has // opened in the middle of the solution close operation. if (_solutionIsClosing) { return; } // We need to push these projects and any project dependencies we already know about. Therefore, compute the // transitive closure of the projects that haven't already been pushed, keeping them in appropriate order. var visited = new HashSet <AbstractProject>(); var inOrderToPush = new List <AbstractProject>(); void addToInOrderToPush(AbstractProject project) { Contract.ThrowIfFalse(ContainsProject(project)); // Bail out if any of the following is true: // 1. We have already started pushing changes for this project OR // 2. We have already visited this project in a prior recursive call if (_pushedProjects.Contains(project) || !visited.Add(project)) { return; } foreach (var projectReference in project.GetCurrentProjectReferences()) { addToInOrderToPush(GetProject(projectReference.ProjectId)); } inOrderToPush.Add(project); } foreach (var project in projects) { addToInOrderToPush(project); } var projectInfos = inOrderToPush.Select(p => p.CreateProjectInfoForCurrentState()).ToImmutableArray(); // We need to enable projects to start pushing changes to the workspace even before we add the solution/project to the host. // This is required because between the point we capture the project info for current state and the point where we start pushing to the workspace, // project system may send new events on the AbstractProject on a background thread, and these won't get pushed over to the workspace hosts as we didn't set the _pushingChangesToWorkspaceHost flag on the AbstractProject. // By invoking StartPushingToWorkspaceHosts upfront, any project state changes on the background thread will enqueue notifications to workspace hosts on foreground scheduled tasks. foreach (var project in inOrderToPush) { project.PushingChangesToWorkspace = true; Logger.Log(FunctionId.AbstractProject_PushedToWorkspace, KeyValueLogMessage.Create(LogType.Trace, m => { m[AbstractProject.ProjectGuidPropertyName] = project.Guid; })); } using (WorkspaceServices.GetService <IGlobalOperationNotificationService>()?.Start("Add Project to Workspace")) { if (!_solutionAdded) { string solutionFilePath = null; VersionStamp?version = default; // Figure out the solution version if (ErrorHandler.Succeeded(_vsSolution.GetSolutionInfo(out var solutionDirectory, out var solutionFileName, out var userOptsFile)) && solutionFileName != null) { solutionFilePath = Path.Combine(solutionDirectory, solutionFileName); if (File.Exists(solutionFilePath)) { version = VersionStamp.Create(File.GetLastWriteTimeUtc(solutionFilePath)); } } if (version == null) { version = VersionStamp.Create(); } var id = SolutionId.CreateNewId(string.IsNullOrWhiteSpace(solutionFileName) ? null : solutionFileName); var solutionInfo = SolutionInfo.Create(id, version.Value, solutionFilePath, projects: projectInfos); NotifyWorkspace(workspace => workspace.OnSolutionAdded(solutionInfo)); _solutionAdded = true; var persistenceService = WorkspaceServices.GetRequiredService <IPersistentStorageLocationService>() as VisualStudioPersistentStorageLocationService; persistenceService?.UpdateForVisualStudioWorkspace(_workspace); } else { // The solution is already added, so we'll just do project added notifications from here foreach (var projectInfo in projectInfos) { NotifyWorkspace(workspace => workspace.OnProjectAdded(projectInfo)); } } foreach (var project in inOrderToPush) { _pushedProjects.Add(project); foreach (var document in project.GetCurrentDocuments()) { if (document.IsOpen) { NotifyWorkspace(workspace => { workspace.OnDocumentOpened( document.Id, document.GetOpenTextBuffer().AsTextContainer(), isCurrentContext: LinkedFileUtilities.IsCurrentContextHierarchy(document, _runningDocumentTable)); (workspace as VisualStudioWorkspaceImpl)?.ConnectToSharedHierarchyEvents(document); }); } } } } }
private async Task ExecuteAsync(Document document, SnapshotSpan snapshotSpan, ITextView textView) { _threadingContext.ThrowIfNotOnUIThread(); var indicatorFactory = document.Project.Solution.Services.GetRequiredService <IBackgroundWorkIndicatorFactory>(); using var backgroundWorkContext = indicatorFactory.Create( textView, snapshotSpan, DialogText, cancelOnEdit: true, cancelOnFocusLost: true); var cancellationToken = backgroundWorkContext.UserCancellationToken; // We're going to log the same thing on success or failure since this blocks the UI thread. This measurement is // intended to tell us how long we're blocking the user from typing with this action. using var blockLogger = Logger.LogBlock(FunctionId.CommandHandler_Paste_ImportsOnPaste, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken); var addMissingImportsService = document.GetRequiredLanguageService <IAddMissingImportsFeatureService>(); var cleanupOptions = await document.GetCodeCleanupOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); var options = new AddMissingImportsOptions( CleanupOptions: cleanupOptions, HideAdvancedMembers: _globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, document.Project.Language)); var textSpan = snapshotSpan.Span.ToTextSpan(); var updatedDocument = await addMissingImportsService.AddMissingImportsAsync(document, textSpan, options, cancellationToken).ConfigureAwait(false); if (updatedDocument is null) { return; } // Required to switch back to the UI thread to call TryApplyChanges await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); document.Project.Solution.Workspace.TryApplyChanges(updatedDocument.Project.Solution); }
public static KeyValueLogMessage Create(int sessionId, int editSessionId, string error) { return(KeyValueLogMessage.Create(m => CreateEditSessionErrorId(m, sessionId, editSessionId, error))); }
public static KeyValueLogMessage Create(int sessionId, int editSessionId, ValueTuple <ushort, ushort> rudeEdit, bool blocking) { return(KeyValueLogMessage.Create(m => CreateEditSessionRudeEdit(m, sessionId, editSessionId, rudeEdit, blocking))); }
public static KeyValueLogMessage Create(int sessionId, EncDebuggingSessionInfo session) { return(KeyValueLogMessage.Create(m => CreateSessionKeyValue(m, sessionId, session))); }
public static KeyValueLogMessage Create(int sessionId, int editSessionId, EncEditSessionInfo editSession) { return(KeyValueLogMessage.Create(m => CreateSessionEditKeyValue(m, sessionId, editSessionId, editSession))); }
internal async Task SearchAsync() { try { using var navigateToSearch = Logger.LogBlock(FunctionId.NavigateTo_Search, KeyValueLogMessage.Create(LogType.UserAction), _cancellationToken); using var asyncToken = _asyncListener.BeginAsyncOperation(GetType() + ".Search"); _progress.AddItems(_solution.Projects.Count()); var workspace = _solution.Workspace; // If the workspace is tracking documents, use that to prioritize our search // order. That way we provide results for the documents the user is working // on faster than the rest of the solution. var docTrackingService = workspace.Services.GetService <IDocumentTrackingService>(); if (docTrackingService != null) { await SearchProjectsInPriorityOrderAsync(docTrackingService).ConfigureAwait(false); } else { await SearchAllProjectsAsync().ConfigureAwait(false); } } catch (OperationCanceledException) { } finally { var service = _solution.Workspace.Services.GetService <IWorkspaceStatusService>(); if (_callback is INavigateToCallback2 callback2 && !await service.IsFullyLoadedAsync(_cancellationToken).ConfigureAwait(false)) { // providing this extra information will make UI to show indication to users // that result might not contain full data callback2.Done(IncompleteReason.SolutionLoading); }
internal static void LogDebuggingSessionTelemetry(DebuggingSessionTelemetry.Data debugSessionData, Action <FunctionId, LogMessage> log, Func <int> getNextId) { const string SessionId = nameof(SessionId); const string EditSessionId = nameof(EditSessionId); var debugSessionId = getNextId(); log(FunctionId.Debugging_EncSession, KeyValueLogMessage.Create(map => { map[SessionId] = debugSessionId; map["SessionCount"] = debugSessionData.EditSessionData.Length; map["EmptySessionCount"] = debugSessionData.EmptyEditSessionCount; })); foreach (var editSessionData in debugSessionData.EditSessionData) { var editSessionId = getNextId(); log(FunctionId.Debugging_EncSession_EditSession, KeyValueLogMessage.Create(map => { map[SessionId] = debugSessionId; map[EditSessionId] = editSessionId; map["HadCompilationErrors"] = editSessionData.HadCompilationErrors; map["HadRudeEdits"] = editSessionData.HadRudeEdits; map["HadValidChanges"] = editSessionData.HadValidChanges; map["HadValidInsignificantChanges"] = editSessionData.HadValidInsignificantChanges; map["RudeEditsCount"] = editSessionData.RudeEdits.Length; map["EmitDeltaErrorIdCount"] = editSessionData.EmitErrorIds.Length; })); foreach (var errorId in editSessionData.EmitErrorIds) { log(FunctionId.Debugging_EncSession_EditSession_EmitDeltaErrorId, KeyValueLogMessage.Create(map => { map[SessionId] = debugSessionId; map[EditSessionId] = editSessionId; map["ErrorId"] = errorId; })); } foreach (var(editKind, syntaxKind) in editSessionData.RudeEdits) { log(FunctionId.Debugging_EncSession_EditSession_RudeEdit, KeyValueLogMessage.Create(map => { map[SessionId] = debugSessionId; map[EditSessionId] = editSessionId; map["RudeEditKind"] = editKind; map["RudeEditSyntaxKind"] = syntaxKind; map["RudeEditBlocking"] = editSessionData.HadRudeEdits; })); } } }
private static void LogWorkspaceAnalyzerCount(int analyzerCount) { Logger.Log(FunctionId.DiagnosticAnalyzerService_Analyzers, KeyValueLogMessage.Create(m => m["AnalyzerCount"] = analyzerCount)); }
private async Task FindImplementingMembersAsync( Document document, int caretPosition, IStreamingFindUsagesPresenter presenter) { try { using (var token = _asyncListener.BeginAsyncOperation(nameof(FindImplementingMembersAsync))) { // Let the presented know we're starting a search. var context = presenter.StartSearch( EditorFeaturesResources.Navigating, supportsReferences: true); using (Logger.LogBlock( FunctionId.CommandHandler_FindAllReference, KeyValueLogMessage.Create(LogType.UserAction, m => m["type"] = "streaming"), context.CancellationToken)) { try { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task var relevantSymbol = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(document, caretPosition, context.CancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var interfaceSymbol = relevantSymbol?.symbol as INamedTypeSymbol; if (interfaceSymbol == null || interfaceSymbol.TypeKind != TypeKind.Interface) { //looks like it's not a relevant symbol return; } // we now need to find the class that implements this particular interface, at the // caret position, or somewhere around it SyntaxNode nodeRoot; if (!document.TryGetSyntaxRoot(out nodeRoot)) { return; } #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task var syntaxTree = await document.GetSyntaxTreeAsync(); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var documentToken = nodeRoot.FindToken(caretPosition); if (!documentToken.Span.IntersectsWith(caretPosition)) { return; // looks like it's not relevant } // the parents should bring us to the class definition var parentTypeNode = documentToken.Parent?.Parent?.Parent?.Parent; #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task var compilation = await document.Project.GetCompilationAsync(); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task // let's finally get our implementing type var namedTypeSymbol = compilation.GetSemanticModel(syntaxTree).GetDeclaredSymbol(parentTypeNode) as INamedTypeSymbol; // unless something went wrong, and we got an empty symbol, if (namedTypeSymbol == null) { return; } // we can search for implementations of the interface, within this type #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await InspectInterfaceAsync(context, interfaceSymbol, namedTypeSymbol, document.Project); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task // now, we iterate on interfaces of our interfaces foreach (var iFace in interfaceSymbol.AllInterfaces) { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await InspectInterfaceAsync(context, iFace, namedTypeSymbol, document.Project); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task } } finally { await context.OnCompletedAsync().ConfigureAwait(false); } } } } catch (OperationCanceledException) { } catch (Exception e) when(FatalError.ReportAndCatch(e)) { } }
public static void LogDiagnosticsStats(ImmutableDictionary <Project, ImmutableArray <Diagnostic> > projectsAndDiagnosticsToFixMap) { Logger.Log(FunctionId.CodeFixes_FixAllOccurrencesComputation_Diagnostics, KeyValueLogMessage.Create(m => { m[s_projectsWithDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Count; m[s_totalDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Values.Sum(v => v.Length); })); }
private async Task FindExtensionMethodsAsync( Document document, int caretPosition, IStreamingFindUsagesPresenter presenter) { var solution = document.Project.Solution; try { using var token = _asyncListener.BeginAsyncOperation(nameof(FindExtensionMethodsAsync)); var(context, cancellationToken) = presenter.StartSearch(EditorFeaturesResources.Navigating, supportsReferences: true); try { using (Logger.LogBlock( FunctionId.CommandHandler_FindAllReference, KeyValueLogMessage.Create(LogType.UserAction, m => m["type"] = "streaming"), cancellationToken)) { var candidateSymbolProjectPair = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(document, caretPosition, cancellationToken).ConfigureAwait(false); var symbol = candidateSymbolProjectPair?.symbol as INamedTypeSymbol; // if we didn't get the right symbol, just abort if (symbol == null) { return; } if (!document.Project.TryGetCompilation(out var compilation)) { return; } foreach (var type in compilation.Assembly.GlobalNamespace.GetAllTypes(cancellationToken)) { if (!type.MightContainExtensionMethods) { continue; } foreach (var extMethod in type.GetMembers().OfType <IMethodSymbol>().Where(method => method.IsExtensionMethod)) { if (cancellationToken.IsCancellationRequested) { break; } var reducedMethod = extMethod.ReduceExtensionMethod(symbol); if (reducedMethod != null) { var loc = extMethod.Locations.First(); var sourceDefinition = await SymbolFinder.FindSourceDefinitionAsync(reducedMethod, solution, cancellationToken).ConfigureAwait(false); // And if our definition actually is from source, then let's re-figure out what project it came from if (sourceDefinition != null) { var originatingProject = solution.GetProject(sourceDefinition.ContainingAssembly, cancellationToken); var definitionItem = reducedMethod.ToNonClassifiedDefinitionItem(solution, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); } } } } } } finally { await context.OnCompletedAsync(cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception e) when(FatalError.ReportAndCatch(e)) { } }
internal static Solution PreviewChanges( Solution currentSolution, Solution newSolution, string fixAllPreviewChangesTitle, string fixAllTopLevelHeader, FixAllKind fixAllKind, string languageOpt, Workspace workspace, int?correlationId = null, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); var functionId = fixAllKind switch { FixAllKind.CodeFix => FunctionId.CodeFixes_FixAllOccurrencesPreviewChanges, FixAllKind.Refactoring => FunctionId.Refactoring_FixAllOccurrencesPreviewChanges, _ => throw ExceptionUtilities.UnexpectedValue(fixAllKind) }; using (Logger.LogBlock( functionId, KeyValueLogMessage.Create(LogType.UserAction, m => { // only set when correlation id is given // we might not have this info for suppression if (correlationId.HasValue) { m[FixAllLogger.CorrelationId] = correlationId; } }), cancellationToken)) { var glyph = languageOpt == null ? Glyph.Assembly : languageOpt == LanguageNames.CSharp ? Glyph.CSharpProject : Glyph.BasicProject; #if COCOA var previewService = workspace.Services.GetService <IPreviewDialogService>(); // Until IPreviewDialogService is implemented, just execute all changes without user ability to pick and choose if (previewService == null) { return(newSolution); } #else var previewService = workspace.Services.GetRequiredService <IPreviewDialogService>(); #endif var changedSolution = previewService.PreviewChanges( string.Format(EditorFeaturesResources.Preview_Changes_0, fixAllPreviewChangesTitle), "vs.codefix.fixall", fixAllTopLevelHeader, fixAllPreviewChangesTitle, glyph, newSolution, currentSolution); if (changedSolution == null) { // User clicked cancel. FixAllLogger.LogPreviewChangesResult(fixAllKind, correlationId, applied: false); return(null); } FixAllLogger.LogPreviewChangesResult(fixAllKind, correlationId, applied: true, allChangesApplied: changedSolution == newSolution); return(changedSolution); } }