public ProjectCodeModelFactory( VisualStudioWorkspace visualStudioWorkspace, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, IThreadingContext threadingContext, IAsynchronousOperationListenerProvider listenerProvider) : base(threadingContext, assertIsForeground: false) { _visualStudioWorkspace = visualStudioWorkspace; _serviceProvider = serviceProvider; _threadingContext = threadingContext; Listener = listenerProvider.GetListener(FeatureAttribute.CodeModel); // Queue up notifications we hear about docs changing. that way we don't have to fire events multiple times // for the same documents. Once enough time has passed, take the documents that were changed and run // through them, firing their latest events. _documentsToFireEventsFor = new AsyncBatchingWorkQueue <DocumentId>( InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpan, ProcessNextDocumentBatchAsync, // We only care about unique doc-ids, so pass in this comparer to collapse streams of changes for a // single document down to one notification. EqualityComparer <DocumentId> .Default, Listener, threadingContext.DisposalToken); _visualStudioWorkspace.WorkspaceChanged += OnWorkspaceChanged; }
internal VisualStudioInProcLanguageServer( AbstractRequestDispatcherFactory requestDispatcherFactory, JsonRpc jsonRpc, ICapabilitiesProvider capabilitiesProvider, LspWorkspaceRegistrationService workspaceRegistrationService, IGlobalOptionService globalOptions, IAsynchronousOperationListenerProvider listenerProvider, ILspLogger logger, IDiagnosticService?diagnosticService, ImmutableArray <string> supportedLanguages, string?clientName, string userVisibleServerName, string telemetryServerTypeName) : base(requestDispatcherFactory, jsonRpc, capabilitiesProvider, workspaceRegistrationService, lspMiscellaneousFilesWorkspace: null, globalOptions, listenerProvider, logger, supportedLanguages, clientName, userVisibleServerName, telemetryServerTypeName) { _supportedLanguages = supportedLanguages; _diagnosticService = diagnosticService; // Dedupe on DocumentId. If we hear about the same document multiple times, we only need to process that id once. _diagnosticsWorkQueue = new AsyncBatchingWorkQueue <DocumentId>( TimeSpan.FromMilliseconds(250), (ids, ct) => ProcessDiagnosticUpdatedBatchAsync(_diagnosticService, ids, ct), EqualityComparer <DocumentId> .Default, Listener, Queue.CancellationToken); if (_diagnosticService != null) { _diagnosticService.DiagnosticsUpdated += DiagnosticService_DiagnosticsUpdated; } }
private async Task StartWorkerAsync(CancellationToken cancellationToken) { _workQueue = new AsyncBatchingWorkQueue <DesignerAttributeData>( TimeSpan.FromSeconds(1), this.NotifyProjectSystemAsync, cancellationToken); var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) { return; } // Pass ourselves in as the callback target for the OOP service. As it discovers // designer attributes it will call back into us to notify VS about it. _keepAliveSession = await client.TryCreateKeepAliveSessionAsync( WellKnownServiceHubServices.RemoteDesignerAttributeService, callbackTarget : this, cancellationToken).ConfigureAwait(false); if (_keepAliveSession == null) { return; } // Now kick off scanning in the OOP process. var success = await _keepAliveSession.TryInvokeAsync( nameof(IRemoteDesignerAttributeService.StartScanningForDesignerAttributesAsync), solution : null, arguments : Array.Empty <object>(), cancellationToken).ConfigureAwait(false); }
private async Task StartWorkerAsync(CancellationToken cancellationToken) { _workQueue = new AsyncBatchingWorkQueue <DocumentAndComments>( TimeSpan.FromSeconds(1), ProcessTodoCommentInfosAsync, cancellationToken); var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) { return; } // Pass ourselves in as the callback target for the OOP service. As it discovers // todo comments it will call back into us to notify VS about it. _keepAliveSession = await client.TryCreateKeepAliveSessionAsync( WellKnownServiceHubServices.RemoteTodoCommentsService, callbackTarget : this, cancellationToken).ConfigureAwait(false); if (_keepAliveSession == null) { return; } // Now that we've started, let the VS todo list know to start listening to us _eventListenerTracker.EnsureEventListener(_workspace, this); // Now kick off scanning in the OOP process. var success = await _keepAliveSession.TryInvokeAsync( nameof(IRemoteTodoCommentsService.ComputeTodoCommentsAsync), solution : null, arguments : Array.Empty <object>(), cancellationToken).ConfigureAwait(false); }
public SQLitePersistentStorage( string workingFolderPath, string solutionFilePath, string databaseFile, IDisposable dbOwnershipLock, IPersistentStorageFaultInjector faultInjectorOpt) : base(workingFolderPath, solutionFilePath, databaseFile) { _dbOwnershipLock = dbOwnershipLock; _faultInjectorOpt = faultInjectorOpt; _solutionAccessor = new SolutionAccessor(this); _projectAccessor = new ProjectAccessor(this); _documentAccessor = new DocumentAccessor(this); _select_star_from_string_table = $@"select * from {StringInfoTableName}"; _insert_into_string_table_values_0 = $@"insert into {StringInfoTableName}(""{DataColumnName}"") values (?)"; _select_star_from_string_table_where_0_limit_one = $@"select * from {StringInfoTableName} where (""{DataColumnName}"" = ?) limit 1"; // Create a queue to batch up requests to flush. We'll won't flush more than every FlushAllDelayMS. The // actual information in the queue isn't relevant, so we pass in an equality comparer to just keep it down // to storing a single piece of data. _flushQueue = new AsyncBatchingWorkQueue <bool>( TimeSpan.FromMilliseconds(FlushAllDelayMS), FlushInMemoryDataToDiskIfNotShutdownAsync, EqualityComparer <bool> .Default, asyncListener: null, _shutdownTokenSource.Token); }
public ProjectCodeModelFactory( VisualStudioWorkspace visualStudioWorkspace, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, IThreadingContext threadingContext, IForegroundNotificationService notificationService, IAsynchronousOperationListenerProvider listenerProvider) { _visualStudioWorkspace = visualStudioWorkspace; _serviceProvider = serviceProvider; _threadingContext = threadingContext; _deferredCleanupTasks = new JoinableTaskCollection(threadingContext.JoinableTaskContext); _deferredCleanupTasks.DisplayName = nameof(ProjectCodeModelFactory) + "." + nameof(_deferredCleanupTasks); _notificationService = notificationService; _listener = listenerProvider.GetListener(FeatureAttribute.CodeModel); // Queue up notifications we hear about docs changing. that way we don't have to fire events multiple times // for the same documents. Once enough time has passed, take the documents that were changed and run // through them, firing their latest events. _documentsToFireEventsFor = new AsyncBatchingWorkQueue <DocumentId>( TimeSpan.FromMilliseconds(visualStudioWorkspace.Options.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS)), ProcessNextDocumentBatchAsync, // We only care about unique doc-ids, so pass in this comparer to collapse streams of changes for a // single document down to one notification. EqualityComparer <DocumentId> .Default, _listener, _cancellationTokenSource.Token); _visualStudioWorkspace.WorkspaceChanged += OnWorkspaceChanged; }
public ImportCompletionCacheService( ConcurrentDictionary <string, TMetadataCacheEntry> peCache, ConcurrentDictionary <ProjectId, TProjectCacheEntry> projectCache, AsyncBatchingWorkQueue <Project> workQueue) { PEItemsCache = peCache; ProjectItemsCache = projectCache; WorkQueue = workQueue; }
public VisualStudioProjectTelemetryService( VisualStudioWorkspaceImpl workspace, IThreadingContext threadingContext) : base(threadingContext) { _workspace = workspace; _workQueue = new AsyncBatchingWorkQueue <ProjectTelemetryData>( TimeSpan.FromSeconds(1), NotifyTelemetryServiceAsync, threadingContext.DisposalToken); }
public VisualStudioProjectTelemetryService( VisualStudioWorkspaceImpl workspace, IThreadingContext threadingContext, IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) : base(threadingContext) { _workspace = workspace; _workQueue = new AsyncBatchingWorkQueue <ProjectTelemetryData>( TimeSpan.FromSeconds(1), NotifyTelemetryServiceAsync, asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Telemetry), threadingContext.DisposalToken); }
public FileChangeWatcher( IAsynchronousOperationListenerProvider listenerProvider, Task <IVsAsyncFileChangeEx> fileChangeService) { _fileChangeService = fileChangeService; // 📝 Empirical testing during high activity (e.g. solution close) showed strong batching performance even // though the batching delay is 0. _taskQueue = new AsyncBatchingWorkQueue <WatcherOperation>( TimeSpan.Zero, ProcessBatchAsync, listenerProvider.GetListener(FeatureAttribute.Workspace), CancellationToken.None); }
public VisualStudioDesignerAttributeService( VisualStudioWorkspaceImpl workspace, IThreadingContext threadingContext, Shell.SVsServiceProvider serviceProvider) : base(threadingContext) { _workspace = workspace; _serviceProvider = serviceProvider; _workQueue = new AsyncBatchingWorkQueue <DesignerAttributeData>( TimeSpan.FromSeconds(1), this.NotifyProjectSystemAsync, ThreadingContext.DisposalToken); }
public FindUsagesLSPContext( IProgress <VSReferenceItem[]> progress, Document document, int position, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken) { _progress = progress; _document = document; _position = position; _metadataAsSourceFileService = metadataAsSourceFileService; _workQueue = new AsyncBatchingWorkQueue <VSReferenceItem>( TimeSpan.FromMilliseconds(500), ReportReferencesAsync, cancellationToken); CancellationToken = cancellationToken; }
public WorkspaceChangedEventSource( ITextBuffer subjectBuffer, IAsynchronousOperationListener asyncListener) : base(subjectBuffer) { // That will ensure that even if we get a flurry of workspace events that we // only process a tag change once. _asyncDelay = new AsyncBatchingWorkQueue( TimeSpan.FromMilliseconds(250), processBatchAsync: cancellationToken => { RaiseChanged(); return(ValueTaskFactory.CompletedTask); }, asyncListener, CancellationToken.None); }
public InProcLanguageServer( AbstractInProcLanguageClient languageClient, Stream inputStream, Stream outputStream, AbstractRequestHandlerProvider requestHandlerProvider, Workspace workspace, IDiagnosticService?diagnosticService, IAsynchronousOperationListenerProvider listenerProvider, ILspSolutionProvider solutionProvider, string?clientName) { _languageClient = languageClient; _requestHandlerProvider = requestHandlerProvider; _workspace = workspace; var jsonMessageFormatter = new JsonMessageFormatter(); jsonMessageFormatter.JsonSerializer.Converters.Add(new VSExtensionConverter <TextDocumentIdentifier, VSTextDocumentIdentifier>()); jsonMessageFormatter.JsonSerializer.Converters.Add(new VSExtensionConverter <ClientCapabilities, VSClientCapabilities>()); _jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)); _jsonRpc.AddLocalRpcTarget(this); _jsonRpc.StartListening(); _diagnosticService = diagnosticService; _listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); _clientName = clientName; _queue = new RequestExecutionQueue(solutionProvider); _queue.RequestServerShutdown += RequestExecutionQueue_Errored; // Dedupe on DocumentId. If we hear about the same document multiple times, we only need to process that id once. _diagnosticsWorkQueue = new AsyncBatchingWorkQueue <DocumentId>( TimeSpan.FromMilliseconds(250), ProcessDiagnosticUpdatedBatchAsync, EqualityComparer <DocumentId> .Default, _listener, _queue.CancellationToken); if (_diagnosticService != null) { _diagnosticService.DiagnosticsUpdated += DiagnosticService_DiagnosticsUpdated; } }
public WorkspaceChangedEventSource( ITextBuffer subjectBuffer, TaggerDelay delay, IAsynchronousOperationListener asyncListener) : base(subjectBuffer, delay) { // Pass in an equality comparer here. That will ensure that even if we get a flurry of workspace // events that we only ever have one entry in the workqueue as all other events will dedupe against // that one. _workQueue = new AsyncBatchingWorkQueue <bool>( TimeSpan.FromMilliseconds(250), processBatchAsync: (_1, _2) => { RaiseChanged(); return(Task.CompletedTask); }, equalityComparer: EqualityComparer <bool> .Default, asyncListener, CancellationToken.None); }
private InProcLanguageServer( AbstractInProcLanguageClient languageClient, RequestDispatcher requestDispatcher, Workspace workspace, IDiagnosticService?diagnosticService, IAsynchronousOperationListenerProvider listenerProvider, ILspWorkspaceRegistrationService lspWorkspaceRegistrationService, string serverTypeName, string?clientName, JsonRpc jsonRpc, LogHubLspLogger?logger) { _languageClient = languageClient; _requestDispatcher = requestDispatcher; _workspace = workspace; _logger = logger; _jsonRpc = jsonRpc; _jsonRpc.AddLocalRpcTarget(this); _jsonRpc.StartListening(); _diagnosticService = diagnosticService; _listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); _clientName = clientName; _queue = new RequestExecutionQueue(logger ?? NoOpLspLogger.Instance, lspWorkspaceRegistrationService, languageClient.Name, serverTypeName); _queue.RequestServerShutdown += RequestExecutionQueue_Errored; // Dedupe on DocumentId. If we hear about the same document multiple times, we only need to process that id once. _diagnosticsWorkQueue = new AsyncBatchingWorkQueue <DocumentId>( TimeSpan.FromMilliseconds(250), ProcessDiagnosticUpdatedBatchAsync, EqualityComparer <DocumentId> .Default, _listener, _queue.CancellationToken); if (_diagnosticService != null) { _diagnosticService.DiagnosticsUpdated += DiagnosticService_DiagnosticsUpdated; } }
private async Task StartAsync(CancellationToken cancellationToken) { _workQueue = new AsyncBatchingWorkQueue <DesignerInfo>( TimeSpan.FromSeconds(1), this.NotifyProjectSystemAsync, cancellationToken); // Have to catch all exceptions coming through here as this is called from a // fire-and-forget method and we want to make sure nothing leaks out. try { await StartWorkerAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { // Cancellation is normal (during VS closing). Just ignore. } catch (Exception e) when(FatalError.ReportWithoutCrash(e)) { // Otherwise report a watson for any other exception. Don't bring down VS. This is // a BG service we don't want impacting the user experience. } }
public TagSource( ITextView?textView, ITextBuffer subjectBuffer, ITextBufferVisibilityTracker?visibilityTracker, AbstractAsynchronousTaggerProvider <TTag> dataSource, IAsynchronousOperationListener asyncListener) { dataSource.ThreadingContext.ThrowIfNotOnUIThread(); if (dataSource.SpanTrackingMode == SpanTrackingMode.Custom) { throw new ArgumentException("SpanTrackingMode.Custom not allowed.", "spanTrackingMode"); } _textView = textView; _subjectBuffer = subjectBuffer; _visibilityTracker = visibilityTracker; _dataSource = dataSource; _asyncListener = asyncListener; _workspaceRegistration = Workspace.GetWorkspaceRegistration(subjectBuffer.AsTextContainer()); // Collapse all booleans added to just a max of two ('true' or 'false') representing if we're being // asked for initial tags or not _eventChangeQueue = new AsyncBatchingWorkQueue <bool>( dataSource.EventChangeDelay.ComputeTimeDelay(), ProcessEventChangeAsync, EqualityComparer <bool> .Default, asyncListener, _disposalTokenSource.Token); _highPriTagsChangedQueue = new AsyncBatchingWorkQueue <NormalizedSnapshotSpanCollection>( TaggerDelay.NearImmediate.ComputeTimeDelay(), ProcessTagsChangedAsync, equalityComparer: null, asyncListener, _disposalTokenSource.Token); if (_dataSource.AddedTagNotificationDelay == TaggerDelay.NearImmediate) { // if the tagger wants "added tags" to be reported "NearImmediate"ly, then just reuse // the "high pri" queue as that already reports things at that cadence. _normalPriTagsChangedQueue = _highPriTagsChangedQueue; } else { _normalPriTagsChangedQueue = new AsyncBatchingWorkQueue <NormalizedSnapshotSpanCollection>( _dataSource.AddedTagNotificationDelay.ComputeTimeDelay(), ProcessTagsChangedAsync, equalityComparer: null, asyncListener, _disposalTokenSource.Token); } DebugRecordInitialStackTrace(); // Create the tagger-specific events that will cause the tagger to refresh. _eventSource = CreateEventSource(); // any time visibility changes, resume tagging on all taggers. Any non-visible taggers will pause // themselves immediately afterwards. _onVisibilityChanged = () => ResumeIfVisible(); // Now hook up this tagger to all interesting events. Connect(); // Now that we're all hooked up to the events we care about, start computing the initial set of tags at // high priority. We want to get the UI to a complete state as soon as possible. EnqueueWork(highPriority: true); return; // Represented as a local function just so we can keep this in sync with Dispose.Disconnect below. void Connect() { _dataSource.ThreadingContext.ThrowIfNotOnUIThread(); // Register to hear about visibility changes so we can pause/resume this tagger. _visibilityTracker?.RegisterForVisibilityChanges(subjectBuffer, _onVisibilityChanged); _eventSource.Changed += OnEventSourceChanged; if (_dataSource.TextChangeBehavior.HasFlag(TaggerTextChangeBehavior.TrackTextChanges)) { _subjectBuffer.Changed += OnSubjectBufferChanged; } if (_dataSource.CaretChangeBehavior.HasFlag(TaggerCaretChangeBehavior.RemoveAllTagsOnCaretMoveOutsideOfTag)) { if (_textView == null) { throw new ArgumentException( nameof(_dataSource.CaretChangeBehavior) + " can only be specified for an " + nameof(IViewTaggerProvider)); } _textView.Caret.PositionChanged += OnCaretPositionChanged; } // Tell the interaction object to start issuing events. _eventSource.Connect(); } }
public TagSource( ITextView textViewOpt, ITextBuffer subjectBuffer, AbstractAsynchronousTaggerProvider <TTag> dataSource, IAsynchronousOperationListener asyncListener) : base(dataSource.ThreadingContext) { this.AssertIsForeground(); if (dataSource.SpanTrackingMode == SpanTrackingMode.Custom) { throw new ArgumentException("SpanTrackingMode.Custom not allowed.", "spanTrackingMode"); } _subjectBuffer = subjectBuffer; _textViewOpt = textViewOpt; _dataSource = dataSource; _asyncListener = asyncListener; _highPriTagsChangedQueue = new AsyncBatchingWorkQueue <NormalizedSnapshotSpanCollection>( TaggerDelay.NearImmediate.ComputeTimeDelay(), ProcessTagsChangedAsync, equalityComparer: null, asyncListener, _tagSourceState.Target.DisposalToken); if (_dataSource.AddedTagNotificationDelay == TaggerDelay.NearImmediate) { // if the tagger wants "added tags" to be reported "NearImmediate"ly, then just reuse // the "high pri" queue as that already reports things at that cadence. _normalPriTagsChangedQueue = _highPriTagsChangedQueue; } else { _normalPriTagsChangedQueue = new AsyncBatchingWorkQueue <NormalizedSnapshotSpanCollection>( _dataSource.AddedTagNotificationDelay.ComputeTimeDelay(), ProcessTagsChangedAsync, equalityComparer: null, asyncListener, _tagSourceState.Target.DisposalToken); } DebugRecordInitialStackTrace(); _eventSource = CreateEventSource(); Connect(); // Start computing the initial set of tags immediately. We want to get the UI // to a complete state as soon as possible. EnqueueWork(initialTags: true); return; void Connect() { this.AssertIsForeground(); _eventSource.Changed += OnEventSourceChanged; if (_dataSource.TextChangeBehavior.HasFlag(TaggerTextChangeBehavior.TrackTextChanges)) { _subjectBuffer.Changed += OnSubjectBufferChanged; } if (_dataSource.CaretChangeBehavior.HasFlag(TaggerCaretChangeBehavior.RemoveAllTagsOnCaretMoveOutsideOfTag)) { if (_textViewOpt == null) { throw new ArgumentException( nameof(_dataSource.CaretChangeBehavior) + " can only be specified for an " + nameof(IViewTaggerProvider)); } _textViewOpt.Caret.PositionChanged += OnCaretPositionChanged; } // Tell the interaction object to start issuing events. _eventSource.Connect(); } }