public void PostReturn() { _threadingContext.ThrowIfNotOnUIThread(); if (this.GetCaretPosition().HasValue) { var closingSnapshotPoint = ClosingPoint.GetPoint(SubjectBuffer.CurrentSnapshot); if (closingSnapshotPoint.Position > 0 && HasNoForwardTyping(this.GetCaretPosition().Value, closingSnapshotPoint.Subtract(1))) { if (!TryGetBraceCompletionContext(out var context, CancellationToken.None)) { return; } var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, context.Document.LanguageServices, explicitFormat: false); var changesAfterReturn = _service.GetTextChangeAfterReturn(context, indentationOptions, CancellationToken.None); if (changesAfterReturn != null) { using var caretPreservingTransaction = new CaretPreservingEditTransaction(EditorFeaturesResources.Brace_Completion, _undoHistory, _editorOperations); ApplyBraceCompletionResult(changesAfterReturn.Value); caretPreservingTransaction.Complete(); } } } }
/// <returns>The tracking span of the inserted "/**/" if there is an $end$ location, null /// otherwise.</returns> protected override ITrackingSpan InsertEmptyCommentAndGetEndPositionTrackingSpan() { VsTextSpan[] endSpanInSurfaceBuffer = new VsTextSpan[1]; if (ExpansionSession.GetEndSpan(endSpanInSurfaceBuffer) != VSConstants.S_OK) { return(null); } SnapshotSpan subjectBufferEndSpan; if (!TryGetSubjectBufferSpan(endSpanInSurfaceBuffer[0], out subjectBufferEndSpan)) { return(null); } var endPosition = subjectBufferEndSpan.Start.Position; string commentString = "/**/"; SubjectBuffer.Insert(endPosition, commentString); var commentSpan = new Span(endPosition, commentString.Length); return(SubjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive)); }
public void PreBackspace(out bool handledCommand) { handledCommand = false; var caretPos = this.GetCaretPosition(); var snapshot = SubjectBuffer.CurrentSnapshot; if (caretPos.HasValue && caretPos.Value.Position > 0 && (caretPos.Value.Position - 1) == OpeningPoint.GetPoint(snapshot).Position && !HasForwardTyping) { using var undo = CreateUndoTransaction(); using var edit = SubjectBuffer.CreateEdit(); var span = new SnapshotSpan(OpeningPoint.GetPoint(snapshot), ClosingPoint.GetPoint(snapshot)); edit.Delete(span); if (edit.HasFailedChanges) { edit.Cancel(); undo.Cancel(); Debug.Fail("Unable to clear braces"); } else { // handle the command so the backspace does // not go through since we've already cleared the braces handledCommand = true; edit.ApplyAndLogExceptions(); undo.Complete(); EndSession(); } } }
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 OnDisconnect() { this.DataBuffer.Changed -= OnDataBufferChanged; this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer()); this.ContainedDocument.Dispose(); }
private void ApplyBraceCompletionResult(BraceCompletionResult result) { _threadingContext.ThrowIfNotOnUIThread(); using var edit = SubjectBuffer.CreateEdit(); foreach (var change in result.TextChanges) { edit.Replace(change.Span.ToSpan(), change.NewText); } edit.ApplyAndLogExceptions(); try { Contract.ThrowIfFalse(SubjectBuffer.CurrentSnapshot[OpeningPoint.GetPosition(SubjectBuffer.CurrentSnapshot)] == OpeningBrace, "The opening point does not match the opening brace character"); Contract.ThrowIfFalse(SubjectBuffer.CurrentSnapshot[ClosingPoint.GetPosition(SubjectBuffer.CurrentSnapshot) - 1] == ClosingBrace, "The closing point does not match the closing brace character"); } catch (Exception e) when(FatalError.ReportAndCatch(e)) { return; } var caretLine = SubjectBuffer.CurrentSnapshot.GetLineFromLineNumber(result.CaretLocation.Line); TextView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(caretLine, result.CaretLocation.Character)); }
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); }
public void PreOverType(out bool handledCommand) { handledCommand = false; if (ClosingPoint == null) { return; } // Brace completion is not cancellable. var cancellationToken = CancellationToken.None; var snapshot = this.SubjectBuffer.CurrentSnapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); SnapshotPoint closingSnapshotPoint = ClosingPoint.GetPoint(snapshot); if (!HasForwardTyping && _session.AllowOverType(this, cancellationToken)) { SnapshotPoint?caretPos = this.GetCaretPosition(); Debug.Assert(caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position); // ensure that we are within the session before clearing if (caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position && closingSnapshotPoint.Position > 0) { using (ITextUndoTransaction undo = CreateUndoTransaction()) { _editorOperations.AddBeforeTextBufferChangePrimitive(); SnapshotSpan span = new SnapshotSpan(caretPos.Value, closingSnapshotPoint.Subtract(1)); using (ITextEdit edit = SubjectBuffer.CreateEdit()) { edit.Delete(span); if (edit.HasFailedChanges) { Debug.Fail("Unable to clear closing brace"); edit.Cancel(); undo.Cancel(); } else { handledCommand = true; edit.Apply(); MoveCaretToClosingPoint(); _editorOperations.AddAfterTextBufferChangePrimitive(); undo.Complete(); } } } } } }
private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e) { var document = SubjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); if (document != null && document.Id == e.Document.Id) { this.RaiseChanged(); } }
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,
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(); } }
protected override ITrackingSpan InsertEmptyCommentAndGetEndPositionTrackingSpan() { var endSpanInSurfaceBuffer = ExpansionSession.EndSpan; if (!TryGetSubjectBufferSpan(endSpanInSurfaceBuffer, out var subjectBufferEndSpan)) { return(null); } var endPosition = subjectBufferEndSpan.End.Position; var commentString = "/**/"; SubjectBuffer.Insert(endPosition, commentString); var commentSpan = new Span(endPosition, commentString.Length); return(SubjectBuffer.CurrentSnapshot.CreateTrackingSpan(commentSpan, SpanTrackingMode.EdgeExclusive)); }
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(); } } } }
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(); }
public void PreBackspace(out bool handledCommand) { handledCommand = false; SnapshotPoint?caretPos = CaretPosition; ITextSnapshot snapshot = SubjectBuffer.CurrentSnapshot; if (caretPos.HasValue && caretPos.Value.Position > 0 && (caretPos.Value.Position - 1) == _openingPoint.GetPoint(snapshot).Position && !HasForwardTyping) { using (ITextUndoTransaction undo = CreateUndoTransaction()) { using (ITextEdit edit = SubjectBuffer.CreateEdit()) { SnapshotSpan span = new SnapshotSpan(_openingPoint.GetPoint(snapshot), _closingPoint.GetPoint(snapshot)); edit.Delete(span); if (edit.HasFailedChanges) { edit.Cancel(); undo.Cancel(); Debug.Fail("Unable to clear braces"); // just let this backspace proceed normally } else { // handle the command so the backspace does // not go through since we've already cleared the braces handledCommand = true; edit.Apply(); undo.Complete(); EndSession(); } } } } }
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); } } }
private void Start(CancellationToken cancellationToken) { // this is where the caret should go after the change SnapshotPoint pos = TextView.Caret.Position.BufferPosition; ITrackingPoint beforeTrackingPoint = pos.Snapshot.CreateTrackingPoint(pos.Position, PointTrackingMode.Negative); ITextSnapshot snapshot = SubjectBuffer.CurrentSnapshot; SnapshotPoint closingSnapshotPoint = ClosingPoint.GetPoint(snapshot); if (closingSnapshotPoint.Position < 1) { Debug.Fail("The closing point was not found at the expected position."); EndSession(); return; } SnapshotPoint openingSnapshotPoint = closingSnapshotPoint.Subtract(1); if (openingSnapshotPoint.GetChar() != OpeningBrace) { // there is a bug in editor brace completion engine on projection buffer that already fixed in vs_pro. until that is FIed to use // I will make this not to assert // Debug.Fail("The opening brace was not found at the expected position."); EndSession(); return; } OpeningPoint = snapshot.CreateTrackingPoint(openingSnapshotPoint, PointTrackingMode.Positive); var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (!_session.CheckOpeningPoint(this, cancellationToken)) { EndSession(); return; } using (ITextUndoTransaction undo = CreateUndoTransaction()) { // insert the closing brace using (ITextEdit edit = SubjectBuffer.CreateEdit()) { edit.Insert(closingSnapshotPoint, ClosingBrace.ToString()); if (edit.HasFailedChanges) { Debug.Fail("Unable to insert closing brace"); // exit without setting the closing point which will take us off the stack edit.Cancel(); undo.Cancel(); return; } else { snapshot = edit.Apply(); } } SnapshotPoint beforePoint = beforeTrackingPoint.GetPoint(TextView.TextSnapshot); // switch from positive to negative tracking so it stays against the closing brace ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(ClosingPoint.GetPoint(snapshot), PointTrackingMode.Negative); Debug.Assert(ClosingPoint.GetPoint(snapshot).Position > 0 && (new SnapshotSpan(ClosingPoint.GetPoint(snapshot).Subtract(1), 1)) .GetText().Equals(ClosingBrace.ToString()), "The closing point does not match the closing brace character"); // move the caret back between the braces TextView.Caret.MoveTo(beforePoint); _session.AfterStart(this, cancellationToken); undo.Complete(); } }
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; }
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); } } }
private bool TryStart(CancellationToken cancellationToken) { _threadingContext.ThrowIfNotOnUIThread(); var closingSnapshotPoint = ClosingPoint.GetPoint(SubjectBuffer.CurrentSnapshot); if (closingSnapshotPoint.Position < 1) { Debug.Fail("The closing point was not found at the expected position."); return(false); } var openingSnapshotPoint = closingSnapshotPoint.Subtract(1); if (openingSnapshotPoint.GetChar() != OpeningBrace) { // there is a bug in editor brace completion engine on projection buffer that already fixed in vs_pro. until that is FIed to use // I will make this not to assert // Debug.Fail("The opening brace was not found at the expected position."); return(false); } OpeningPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingSnapshotPoint, PointTrackingMode.Positive); var document = SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(false); } var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var context = GetBraceCompletionContext(parsedDocument); // Note: completes synchronously unless Semantic Model is needed to determine the result: if (!_service.HasBraceCompletionAsync(context, document, cancellationToken).WaitAndGetResult(cancellationToken)) { return(false); } var braceResult = _service.GetBraceCompletion(context); using var caretPreservingTransaction = new CaretPreservingEditTransaction(EditorFeaturesResources.Brace_Completion, _undoHistory, _editorOperations); // Apply the change to complete the brace. ApplyBraceCompletionResult(braceResult); // switch the closing point from positive to negative tracking so that the closing point stays against the closing brace ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(ClosingPoint.GetPoint(SubjectBuffer.CurrentSnapshot), PointTrackingMode.Negative); if (TryGetBraceCompletionContext(out var contextAfterStart, cancellationToken)) { var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, contextAfterStart.Document.LanguageServices, explicitFormat: false); var changesAfterStart = _service.GetTextChangesAfterCompletion(contextAfterStart, indentationOptions, cancellationToken); if (changesAfterStart != null) { ApplyBraceCompletionResult(changesAfterStart.Value); } } caretPreservingTransaction.Complete(); return(true); }
public void PreOverType(out bool handledCommand) { _threadingContext.ThrowIfNotOnUIThread(); handledCommand = false; if (ClosingPoint == null) { return; } // Brace completion is not cancellable. var cancellationToken = CancellationToken.None; var snapshot = this.SubjectBuffer.CurrentSnapshot; var closingSnapshotPoint = ClosingPoint.GetPoint(snapshot); if (!HasForwardTyping && AllowOverType()) { var caretPos = this.GetCaretPosition(); Debug.Assert(caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position); // ensure that we are within the session before clearing if (caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position && closingSnapshotPoint.Position > 0) { using var undo = CreateUndoTransaction(); _editorOperations.AddBeforeTextBufferChangePrimitive(); var span = new SnapshotSpan(caretPos.Value, closingSnapshotPoint.Subtract(1)); using var edit = SubjectBuffer.CreateEdit(); edit.Delete(span); if (edit.HasFailedChanges) { Debug.Fail("Unable to clear closing brace"); edit.Cancel(); undo.Cancel(); } else { handledCommand = true; edit.ApplyAndLogExceptions(); MoveCaretToClosingPoint(); _editorOperations.AddAfterTextBufferChangePrimitive(); undo.Complete(); } } } return; bool AllowOverType() { var context = GetBraceCompletionContext(); return(context != null && _service.AllowOverTypeAsync(context.Value, cancellationToken).WaitAndGetResult(cancellationToken)); } }