Example #1
0
        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);
        }
Example #6
0
        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;
        }
Example #7
0
 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);
        }
Example #10
0
        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);
        }
Example #12
0
        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;
        }
Example #13
0
 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);
 }
Example #16
0
        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;
            }
        }
Example #17
0
        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.
            }
        }
Example #18
0
            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();
                }
            }
Example #19
0
            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();
                }
            }