Beispiel #1
0
        private void OnDisconnect()
        {
            this.DataBuffer.Changed -= OnDataBufferChanged;
            this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer());

            this.ContainedDocument.Dispose();
        }
Beispiel #2
0
            private void OnWorkspaceChanged(object?sender, WorkspaceChangeEventArgs e)
            {
                if (e.Kind == WorkspaceChangeKind.ProjectChanged)
                {
                    RoslynDebug.AssertNotNull(e.ProjectId);
                    var oldProject = e.OldSolution.GetRequiredProject(e.ProjectId);
                    var newProject = e.NewSolution.GetRequiredProject(e.ProjectId);

                    if (!object.Equals(oldProject.ParseOptions, newProject.ParseOptions))
                    {
                        var workspace  = e.NewSolution.Workspace;
                        var documentId = workspace.GetDocumentIdInCurrentContext(
                            SubjectBuffer.AsTextContainer()
                            );
                        if (documentId != null)
                        {
                            var relatedDocumentIds = e.NewSolution.GetRelatedDocumentIds(
                                documentId
                                );

                            if (relatedDocumentIds.Any(d => d.ProjectId == e.ProjectId))
                            {
                                RaiseChanged();
                            }
                        }
                    }
                }
            }
            private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e)
            {
                var document = SubjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext();

                if (document != null && document.Id == e.Document.Id)
                {
                    this.RaiseChanged();
                }
            }
Beispiel #4
0
        public override async Task<object> GetPreviewAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            // Light bulb will always invoke this function on the UI thread.
            AssertIsForeground();

            var previewPaneService = Workspace.Services.GetService<IPreviewPaneService>();
            if (previewPaneService == null)
            {
                return null;
            }

            // after this point, this method should only return at GetPreviewPane. otherwise, DifferenceViewer will leak
            // since there is no one to close the viewer
            var preferredDocumentId = Workspace.GetDocumentIdInCurrentContext(
                SubjectBuffer.AsTextContainer()
            );
            var preferredProjectId = preferredDocumentId?.ProjectId;

            var extensionManager = this.Workspace.Services.GetService<IExtensionManager>();
            var previewContents = await extensionManager
                .PerformFunctionAsync(
                    Provider,
                    async () =>
                    {
                        // We need to stay on UI thread after GetPreviewResultAsync() so that TakeNextPreviewAsync()
                        // below can execute on UI thread. We use ConfigureAwait(true) to stay on the UI thread.
                        var previewResult = await GetPreviewResultAsync(cancellationToken)
                            .ConfigureAwait(true);
                        if (previewResult == null)
                        {
                            return null;
                        }
                        else
                        {
                            // TakeNextPreviewAsync() needs to run on UI thread.
                            AssertIsForeground();
                            return await previewResult
                                .GetPreviewsAsync(
                                    preferredDocumentId,
                                    preferredProjectId,
                                    cancellationToken
                                )
                                .ConfigureAwait(true);
                        }
                        // GetPreviewPane() below needs to run on UI thread. We use ConfigureAwait(true) to stay on the UI thread.
                    },
                    defaultValue: null
                )
                .ConfigureAwait(true);

            // GetPreviewPane() needs to run on the UI thread.
            AssertIsForeground();

            return previewPaneService.GetPreviewPane(GetDiagnostic(), previewContents);
        }
Beispiel #5
0
        internal ContainedLanguage(
            IVsTextBufferCoordinator bufferCoordinator,
            IComponentModel componentModel,
            VisualStudioProject project,
            IVsHierarchy hierarchy,
            uint itemid,
            VisualStudioProjectTracker projectTrackerOpt,
            ProjectId projectId,
            TLanguageService languageService,
            AbstractFormattingRule vbHelperFormattingRule = null)
        {
            this.BufferCoordinator = bufferCoordinator;
            this.ComponentModel    = componentModel;
            this.Project           = project;
            _languageService       = languageService;

            this.Workspace = projectTrackerOpt?.Workspace ?? componentModel.GetService <VisualStudioWorkspace>();

            _editorAdaptersFactoryService = componentModel.GetService <IVsEditorAdaptersFactoryService>();
            _diagnosticAnalyzerService    = componentModel.GetService <IDiagnosticAnalyzerService>();

            // Get the ITextBuffer for the secondary buffer
            Marshal.ThrowExceptionForHR(bufferCoordinator.GetSecondaryBuffer(out var secondaryTextLines));
            var secondaryVsTextBuffer = (IVsTextBuffer)secondaryTextLines;

            SubjectBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(secondaryVsTextBuffer);

            // Get the ITextBuffer for the primary buffer
            Marshal.ThrowExceptionForHR(bufferCoordinator.GetPrimaryBuffer(out var primaryTextLines));
            DataBuffer = _editorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)primaryTextLines);

            // Create our tagger
            var bufferTagAggregatorFactory = ComponentModel.GetService <IBufferTagAggregatorFactoryService>();

            _bufferTagAggregator = bufferTagAggregatorFactory.CreateTagAggregator <ITag>(SubjectBuffer);

            if (!ErrorHandler.Succeeded(((IVsProject)hierarchy).GetMkDocument(itemid, out var filePath)))
            {
                // we couldn't look up the document moniker from an hierarchy for an itemid.
                // Since we only use this moniker as a key, we could fall back to something else, like the document name.
                Debug.Assert(false, "Could not get the document moniker for an item from its hierarchy.");
                if (!hierarchy.TryGetItemName(itemid, out filePath))
                {
                    FatalError.Report(new System.Exception("Failed to get document moniker for a contained document"));
                }
            }

            DocumentId documentId;

            if (this.Project != null)
            {
                documentId = this.Project.AddSourceTextContainer(
                    SubjectBuffer.AsTextContainer(), filePath,
                    sourceCodeKind: SourceCodeKind.Regular, folders: default,
Beispiel #6
0
        internal ContainedLanguage(
            IVsTextBufferCoordinator bufferCoordinator,
            IComponentModel componentModel,
            Workspace workspace,
            ProjectId projectId,
            VisualStudioProject?project,
            string filePath,
            Guid languageServiceGuid,
            AbstractFormattingRule?vbHelperFormattingRule = null
            )
        {
            this.BufferCoordinator = bufferCoordinator;
            this.ComponentModel    = componentModel;
            this.Project           = project;
            _languageServiceGuid   = languageServiceGuid;

            this.Workspace = workspace;

            _editorAdaptersFactoryService =
                componentModel.GetService <IVsEditorAdaptersFactoryService>();
            _diagnosticAnalyzerService = componentModel.GetService <IDiagnosticAnalyzerService>();

            // Get the ITextBuffer for the secondary buffer
            Marshal.ThrowExceptionForHR(
                bufferCoordinator.GetSecondaryBuffer(out var secondaryTextLines)
                );
            SubjectBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(secondaryTextLines) !;

            // Get the ITextBuffer for the primary buffer
            Marshal.ThrowExceptionForHR(
                bufferCoordinator.GetPrimaryBuffer(out var primaryTextLines)
                );
            DataBuffer = _editorAdaptersFactoryService.GetDataBuffer(primaryTextLines) !;

            // Create our tagger
            var bufferTagAggregatorFactory =
                ComponentModel.GetService <IBufferTagAggregatorFactoryService>();

            _bufferTagAggregator = bufferTagAggregatorFactory.CreateTagAggregator <ITag>(
                SubjectBuffer
                );

            DocumentId documentId;

            if (this.Project != null)
            {
                documentId = this.Project.AddSourceTextContainer(
                    SubjectBuffer.AsTextContainer(),
                    filePath,
                    sourceCodeKind: SourceCodeKind.Regular,
                    folders: default,
            private void OnSemanticChanged(object sender, Document d)
            {
                var workspace = this.CurrentWorkspace;

                if (d.Project.Solution.Workspace != workspace)
                {
                    return;
                }

                var documentIds = workspace.GetRelatedDocumentIds(SubjectBuffer.AsTextContainer());

                if (documentIds.Contains(d.Id))
                {
                    this.RaiseChanged();
                }
            }
            private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
            {
                if (e.Kind == WorkspaceChangeKind.ProjectChanged)
                {
                    var oldProject = e.OldSolution.GetProject(e.ProjectId);
                    var newProject = e.NewSolution.GetProject(e.ProjectId);

                    if (!object.Equals(oldProject.ParseOptions, newProject.ParseOptions))
                    {
                        var workspace   = e.NewSolution.Workspace;
                        var documentIds = workspace.GetRelatedDocumentIds(SubjectBuffer.AsTextContainer());

                        if (documentIds.Any(d => d.ProjectId == e.ProjectId))
                        {
                            this.RaiseChanged();
                        }
                    }
                }
            }
Beispiel #9
0
        private void OnDisconnect()
        {
            this.DataBuffer.Changed -= OnDataBufferChanged;

            if (this.Project != null)
            {
                this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer());
            }
            else
            {
                // It's possible the host of the workspace might have already removed the entire project
                if (Workspace.CurrentSolution.ContainsDocument(ContainedDocument.Id))
                {
                    Workspace.OnDocumentRemoved(ContainedDocument.Id);
                }
            }

            this.ContainedDocument.Dispose();
        }
            private void OnOpenedDocumentSemanticChanged(object sender, Document document)
            {
                var workspace = this.CurrentWorkspace;

                if (document.Project.Solution.Workspace != workspace)
                {
                    return;
                }

                var documentIds = workspace.GetRelatedDocumentIds(SubjectBuffer.AsTextContainer());

                if (!documentIds.Contains(document.Id))
                {
                    return;
                }

                // Semantics may change for a document for two reasons.  One is a top level change
                // outside of this document.  The other is that a change happened inside the document.
                // In the latter case we do *not* want to report a change because we'll already have
                // done so inside of OnSubjectBufferChanged.
                //
                // Note: although we're passing CancellationToken.None here, this should never actually
                // block.  This is because we would have only gotten this notification if this value
                // was already computed.  In which case retrieving it again should happen immediately.
                var documentVersion = document.GetTopLevelChangeTextVersionAsync(CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None);
                var projectVersion  = document.Project.GetDependentSemanticVersionAsync(CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None);

                if (documentVersion == projectVersion)
                {
                    // The semantic version notification was caused by a change to this document.
                    // In which case we want to *ignore* it as we will have already processed its
                    // buffer change event.
                    return;
                }

                // The semantic version notification was caused by something else (a sibling document
                // changing at the top level, or a dependent project changing), we want to report this
                // so that this document can be retagged.
                this.RaiseChanged();
            }
Beispiel #11
0
        private void ExecuteBackspaceOrDelete(ITextView textView, Action nextHandler, bool isDelete)
        {
            AssertIsForeground();

            char?deletedChar;
            var  subjectBufferCaretPoint = GetCaretPointInSubjectBuffer();
            var  viewBufferCaretPoint    = GetCaretPointInViewBuffer();

            if (isDelete)
            {
                deletedChar = viewBufferCaretPoint.Position >= 0 && viewBufferCaretPoint.Position < textView.TextBuffer.CurrentSnapshot.Length
                    ? textView.TextBuffer.CurrentSnapshot[viewBufferCaretPoint.Position]
                    : default;
            }
            else
            {
                // backspace
                deletedChar = viewBufferCaretPoint > 0
                    ? textView.TextBuffer.CurrentSnapshot[viewBufferCaretPoint - 1]
                    : default;
            }

            if (sessionOpt == null)
            {
                // No computation. Disconnect from caret position changes, send the backspace through,
                // and start a computation.
                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var trigger           = CompletionTrigger.CreateDeletionTrigger(deletedChar.GetValueOrDefault());
                var completionService = this.GetCompletionService();

                if (completionService != null)
                {
                    this.StartNewModelComputation(completionService, trigger);
                }

                return;
            }
            else
            {
                var textBeforeDeletion     = SubjectBuffer.AsTextContainer().CurrentText;
                var documentBeforeDeletion = textBeforeDeletion.GetDocumentWithFrozenPartialSemantics(CancellationToken.None);

                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var model = sessionOpt.Computation.InitialUnfilteredModel;

                if ((model == null && CaretHasLeftDefaultTrackingSpan(subjectBufferCaretPoint, documentBeforeDeletion)) ||
                    (model != null && this.IsCaretOutsideAllItemBounds(model, this.GetCaretPointInViewBuffer())) ||
                    (model != null && model.OriginalList.Rules.DismissIfLastCharacterDeleted && AllFilterTextsEmpty(model, GetCaretPointInViewBuffer())))
                {
                    // If the caret moved out of bounds of our items, then we want to dismiss the list.
                    this.DismissSessionIfActive();
                    return;
                }
                else if (model != null)
                {
                    sessionOpt.FilterModel(CompletionFilterReason.Deletion, filterState: null);
                }
            }
        }
        private void ExecuteBackspaceOrDelete(ITextView textView, Action nextHandler, bool isDelete)
        {
            AssertIsForeground();

            char?deletedChar;
            var  subjectBufferCaretPoint = GetCaretPointInSubjectBuffer();
            var  viewBufferCaretPoint    = GetCaretPointInViewBuffer();

            if (isDelete)
            {
                deletedChar = viewBufferCaretPoint.Position >= 0 && viewBufferCaretPoint.Position < textView.TextBuffer.CurrentSnapshot.Length
                    ? textView.TextBuffer.CurrentSnapshot[viewBufferCaretPoint.Position]
                    : default(char?);
            }
            else
            {
                // backspace
                deletedChar = viewBufferCaretPoint > 0
                    ? textView.TextBuffer.CurrentSnapshot[viewBufferCaretPoint - 1]
                    : default(char?);
            }

            if (sessionOpt == null)
            {
                // No computation. Disconnect from caret position changes, send the backspace through,
                // and start a computation.
                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var trigger           = CompletionTrigger.CreateDeletionTrigger(deletedChar.GetValueOrDefault());
                var completionService = this.GetCompletionService();

                if (completionService != null)
                {
                    this.StartNewModelComputation(
                        completionService, trigger, filterItems: false, dismissIfEmptyAllowed: true);
                }

                return;
            }
            else
            {
                var textBeforeDeletion     = SubjectBuffer.AsTextContainer().CurrentText;
                var documentBeforeDeletion = textBeforeDeletion.GetDocumentWithFrozenPartialSemanticsAsync(CancellationToken.None)
                                             .WaitAndGetResult(CancellationToken.None);

                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var model = sessionOpt.Computation.InitialUnfilteredModel;

                if ((model == null && CaretHasLeftDefaultTrackingSpan(subjectBufferCaretPoint, documentBeforeDeletion)) ||
                    (model != null && this.IsCaretOutsideAllItemBounds(model, this.GetCaretPointInViewBuffer())) ||
                    (model != null && model.OriginalList.Rules.DismissIfLastCharacterDeleted && AllFilterTextsEmpty(model, GetCaretPointInViewBuffer())))
                {
                    // If the caret moved out of bounds of our items, then we want to dismiss the list.
                    this.StopModelComputation();
                    return;
                }
                else if (model != null)
                {
                    // If we were triggered on backspace/delete, and we're still deleting,
                    // then we don't want to filter out items (i.e. we still want all items).
                    // However, we do still want to run the code to figure out what the best
                    // item is to select from all those items.
                    FilterToSomeOrAllItems(
                        filterItems: model.Trigger.Kind != CompletionTriggerKind.Deletion,
                        dismissIfEmptyAllowed: true,
                        filterReason: CompletionFilterReason.BackspaceOrDelete);
                }
            }
        }
Beispiel #13
0
        private void ExecuteBackspaceOrDelete(ITextView textView, Action nextHandler, bool isDelete)
        {
            AssertIsForeground();

            char?deletedChar;
            var  caretPoint = GetCaretPointInViewBuffer().Position;

            if (isDelete)
            {
                deletedChar = GetCaretPointInViewBuffer().Position >= 0 && GetCaretPointInViewBuffer().Position < textView.TextBuffer.CurrentSnapshot.Length
                    ? textView.TextBuffer.CurrentSnapshot[GetCaretPointInViewBuffer().Position]
                    : default(char?);
            }
            else
            {
                // backspace
                deletedChar = caretPoint > 0
                    ? textView.TextBuffer.CurrentSnapshot[caretPoint - 1]
                    : default(char?);
            }

            if (sessionOpt == null)
            {
                // No computation. Disconnect from caret position changes, send the backspace through,
                // and start a computation.
                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var triggerInfo       = CompletionTriggerInfo.CreateBackspaceTriggerInfo(deletedChar);
                var completionService = this.CreateCompletionService();

                this.StartNewModelComputation(completionService, triggerInfo, filterItems: false);

                return;
            }
            else
            {
                var textBeforeDeletion     = SubjectBuffer.AsTextContainer().CurrentText;
                var documentBeforeDeletion = textBeforeDeletion.GetDocumentWithFrozenPartialSemanticsAsync(CancellationToken.None)
                                             .WaitAndGetResult(CancellationToken.None);

                this.TextView.TextBuffer.PostChanged -= OnTextViewBufferPostChanged;
                this.TextView.Caret.PositionChanged  -= OnCaretPositionChanged;
                try
                {
                    nextHandler();
                }
                finally
                {
                    this.TextView.TextBuffer.PostChanged += OnTextViewBufferPostChanged;
                    this.TextView.Caret.PositionChanged  += OnCaretPositionChanged;
                }

                var model = sessionOpt.Computation.InitialUnfilteredModel;

                if ((model == null && CaretHasLeftDefaultTrackingSpan(caretPoint, documentBeforeDeletion)) ||
                    (model != null && this.IsCaretOutsideAllItemBounds(model, this.GetCaretPointInViewBuffer())) ||
                    (model != null && CreateCompletionService().DismissIfLastFilterCharacterDeleted&& AllFilterTextsEmpty(model, GetCaretPointInViewBuffer())))
                {
                    // If the caret moved out of bounds of our items, then we want to dismiss the list.
                    this.StopModelComputation();
                    return;
                }
                else if (model != null && model.TriggerInfo.TriggerReason != CompletionTriggerReason.BackspaceOrDeleteCommand)
                {
                    // Filter the model if it wasn't invoked on backspace.
                    sessionOpt.FilterModel(CompletionFilterReason.BackspaceOrDelete);
                }
            }
        }
Beispiel #14
0
        public ContainedLanguage(
            IVsTextBufferCoordinator bufferCoordinator,
            IComponentModel componentModel,
            VisualStudioProject project,
            IVsHierarchy hierarchy,
            uint itemid,
            TLanguageService languageService,
            IFormattingRule vbHelperFormattingRule = null)
        {
            this.BufferCoordinator = bufferCoordinator;
            this.ComponentModel    = componentModel;
            this.Project           = project;
            _languageService       = languageService;

            this.Workspace = componentModel.GetService <VisualStudioWorkspace>();

            _editorAdaptersFactoryService = componentModel.GetService <IVsEditorAdaptersFactoryService>();
            _diagnosticAnalyzerService    = componentModel.GetService <IDiagnosticAnalyzerService>();

            // Get the ITextBuffer for the secondary buffer
            Marshal.ThrowExceptionForHR(bufferCoordinator.GetSecondaryBuffer(out var secondaryTextLines));
            var secondaryVsTextBuffer = (IVsTextBuffer)secondaryTextLines;

            SubjectBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(secondaryVsTextBuffer);

            // Get the ITextBuffer for the primary buffer
            Marshal.ThrowExceptionForHR(bufferCoordinator.GetPrimaryBuffer(out var primaryTextLines));
            DataBuffer = _editorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)primaryTextLines);

            // Create our tagger
            var bufferTagAggregatorFactory = ComponentModel.GetService <IBufferTagAggregatorFactoryService>();

            _bufferTagAggregator = bufferTagAggregatorFactory.CreateTagAggregator <ITag>(SubjectBuffer);

            if (!ErrorHandler.Succeeded(((IVsProject)hierarchy).GetMkDocument(itemid, out var filePath)))
            {
                // we couldn't look up the document moniker from an hierarchy for an itemid.
                // Since we only use this moniker as a key, we could fall back to something else, like the document name.
                Debug.Assert(false, "Could not get the document moniker for an item from its hierarchy.");
                if (!hierarchy.TryGetItemName(itemid, out filePath))
                {
                    FatalError.Report(new System.Exception("Failed to get document moniker for a contained document"));
                }
            }

            var documentId = this.Project.AddSourceTextContainer(SubjectBuffer.AsTextContainer(), filePath);

            this.ContainedDocument = new ContainedDocument(
                componentModel.GetService <IThreadingContext>(),
                documentId,
                subjectBuffer: SubjectBuffer,
                dataBuffer: DataBuffer,
                bufferCoordinator,
                this.Workspace,
                project,
                hierarchy,
                itemid,
                componentModel,
                vbHelperFormattingRule);

            // TODO: Can contained documents be linked or shared?
            this.DataBuffer.Changed += OnDataBufferChanged;
        }