public bool ExecuteCommand(GotoBraceCommandArgs args, VSCommanding.CommandExecutionContext executionContext) { var snapshot = args.SubjectBuffer.CurrentSnapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); var options = BraceMatchingOptions.From(document.Project); var caretPosition = args.TextView.Caret.Position.BufferPosition.Position; var task = _braceMatchingService.FindMatchingSpanAsync(document, caretPosition, options, executionContext.OperationContext.UserCancellationToken); var span = task.WaitAndGetResult(executionContext.OperationContext.UserCancellationToken); if (!span.HasValue) { return(false); } if (span.Value.Start < caretPosition) { args.TextView.TryMoveCaretToAndEnsureVisible(args.SubjectBuffer.CurrentSnapshot.GetPoint(span.Value.Start)); } else if (span.Value.End > caretPosition) { args.TextView.TryMoveCaretToAndEnsureVisible(args.SubjectBuffer.CurrentSnapshot.GetPoint(span.Value.End)); } return(true); }
protected override Task ProduceTagsAsync( TaggerContext <BraceHighlightTag> context, DocumentSnapshotSpan documentSnapshotSpan, int?caretPosition, CancellationToken cancellationToken) { var document = documentSnapshotSpan.Document; if (!caretPosition.HasValue || document == null) { return(Task.CompletedTask); } var options = BraceMatchingOptions.From(document.Project); return(ProduceTagsAsync( context, document, documentSnapshotSpan.SnapshotSpan.Snapshot, caretPosition.Value, options, cancellationToken)); }
protected async Task TestAsync(string markup, string expectedCode, ParseOptions options = null) { using (var workspace = CreateWorkspaceFromCode(markup, options)) { var position = workspace.Documents.Single().CursorPosition.Value; var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id); var braceMatcher = workspace.GetService <IBraceMatchingService>(); var braceMatchingOptions = BraceMatchingOptions.From(document.Project); var foundSpan = await braceMatcher.FindMatchingSpanAsync(document, position, braceMatchingOptions, CancellationToken.None); MarkupTestFile.GetSpans(expectedCode, out var parsedExpectedCode, out ImmutableArray <TextSpan> expectedSpans); if (expectedSpans.Any()) { Assert.Equal(expectedSpans.Single(), foundSpan.Value); } else { Assert.False(foundSpan.HasValue); } } }
internal static int GetPairExtentsWorker(ITextView textView, IBraceMatchingService braceMatcher, int iLine, int iIndex, TextSpan[] pSpan, bool extendSelection, CancellationToken cancellationToken) { pSpan[0].iStartLine = pSpan[0].iEndLine = iLine; pSpan[0].iStartIndex = pSpan[0].iEndIndex = iIndex; var pointInViewBuffer = textView.TextSnapshot.GetLineFromLineNumber(iLine).Start + iIndex; var subjectBuffer = textView.GetBufferContainingCaret(); if (subjectBuffer != null) { // PointTrackingMode and PositionAffinity chosen arbitrarily. var positionInSubjectBuffer = textView.BufferGraph.MapDownToBuffer(pointInViewBuffer, PointTrackingMode.Positive, subjectBuffer, PositionAffinity.Successor); if (!positionInSubjectBuffer.HasValue) { positionInSubjectBuffer = textView.BufferGraph.MapDownToBuffer(pointInViewBuffer, PointTrackingMode.Positive, subjectBuffer, PositionAffinity.Predecessor); } if (positionInSubjectBuffer.HasValue) { var position = positionInSubjectBuffer.Value; var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { var options = BraceMatchingOptions.From(document.Project); var matchingSpan = braceMatcher.FindMatchingSpanAsync(document, position, options, cancellationToken).WaitAndGetResult(cancellationToken); if (matchingSpan.HasValue) { var resultsInView = textView.GetSpanInView(matchingSpan.Value.ToSnapshotSpan(subjectBuffer.CurrentSnapshot)).ToList(); if (resultsInView.Count == 1) { var vsTextSpan = resultsInView[0].ToVsTextSpan(); // caret is at close parenthesis if (matchingSpan.Value.Start < position) { pSpan[0].iStartLine = vsTextSpan.iStartLine; pSpan[0].iStartIndex = vsTextSpan.iStartIndex; // For text selection using goto matching brace, tweak spans to suit the VS editor's behavior. // The vs editor sets selection for GotoBraceExt (Ctrl + Shift + ]) like so : // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // if (fExtendSelection) // { // textSpan.iEndIndex++; // this.SetSelection(textSpan.iStartLine, textSpan.iStartIndex, textSpan.iEndLine, textSpan.iEndIndex); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Notice a couple of things: it arbitrarily increments EndIndex by 1 and does nothing similar for StartIndex. // So, if we're extending selection: // case a: set EndIndex to left of closing parenthesis -- ^} // this adjustment is for any of the four cases where caret could be. left or right of open or close parenthesis -- ^{^ ^}^ // case b: set StartIndex to left of opening parenthesis -- ^{ // this adjustment is for cases where caret was originally to the right of the open parenthesis -- {^ } // if selecting, adjust end position by using the matching opening span that we just computed. if (extendSelection) { // case a. var closingSpans = braceMatcher.FindMatchingSpanAsync(document, matchingSpan.Value.Start, options, cancellationToken).WaitAndGetResult(cancellationToken); var vsClosingSpans = textView.GetSpanInView(closingSpans.Value.ToSnapshotSpan(subjectBuffer.CurrentSnapshot)).ToList().First().ToVsTextSpan(); pSpan[0].iEndIndex = vsClosingSpans.iStartIndex; } } else if (matchingSpan.Value.End > position) // caret is at open parenthesis { pSpan[0].iEndLine = vsTextSpan.iEndLine; pSpan[0].iEndIndex = vsTextSpan.iEndIndex; // if selecting, adjust start position by using the matching closing span that we computed if (extendSelection) { // case a. pSpan[0].iEndIndex = vsTextSpan.iStartIndex; // case b. var openingSpans = braceMatcher.FindMatchingSpanAsync(document, matchingSpan.Value.End, options, cancellationToken).WaitAndGetResult(cancellationToken); var vsOpeningSpans = textView.GetSpanInView(openingSpans.Value.ToSnapshotSpan(subjectBuffer.CurrentSnapshot)).ToList().First().ToVsTextSpan(); pSpan[0].iStartIndex = vsOpeningSpans.iStartIndex; } } } } } } } return(VSConstants.S_OK); }