public async Task <BraceMatchingResult?> FindBracesAsync(
            Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var languagesProvider = document.GetLanguageService <IEmbeddedLanguagesProvider>();

            if (languagesProvider != null)
            {
                foreach (var language in languagesProvider.Languages)
                {
                    var braceMatcher = (language as IEmbeddedLanguageEditorFeatures)?.BraceMatcher;
                    if (braceMatcher != null)
                    {
                        var result = await braceMatcher.FindBracesAsync(
                            document, position, options, cancellationToken).ConfigureAwait(false);

                        if (result != null)
                        {
                            return(result);
                        }
                    }
                }
            }

            return(null);
        }
Example #2
0
        public async Task <BraceMatchingResult?> FindBracesAsync(
            Document document,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            if (position < text.Length && this.IsBrace(text[position]))
            {
                if (token.RawKind == _openBrace.Kind && AllowedForToken(token))
                {
                    var leftToken = token;
                    if (TryFindMatchingToken(leftToken, out var rightToken))
                    {
                        return(new BraceMatchingResult(leftToken.Span, rightToken.Span));
                    }
                }
                else if (token.RawKind == _closeBrace.Kind && AllowedForToken(token))
                {
                    var rightToken = token;
                    if (TryFindMatchingToken(rightToken, out var leftToken))
                    {
                        return(new BraceMatchingResult(leftToken.Span, rightToken.Span));
                    }
                }
            }

            return(null);
        }
Example #3
0
        public async Task <BraceMatchingResult?> FindBracesAsync(
            Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            if (!options.HighlightRelatedJsonComponentsUnderCursor)
            {
                return(null);
            }

            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var detector = JsonLanguageDetector.GetOrCreate(semanticModel.Compilation, _info);

            // We do support brace matching in strings that look very likely to be json, even if we aren't 100% certain
            // if it truly is json.
            var tree = detector.TryParseString(token, semanticModel, includeProbableStrings: true, cancellationToken);

            if (tree == null)
            {
                return(null);
            }

            return(GetMatchingBraces(tree, position));
        }
        public async Task <BraceMatchingResult?> FindBracesAsync(
            Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);

            if (!this.SyntaxTokenKinds.Contains(token.RawKind))
            {
                return(null);
            }

            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var braceMatchers = GetServices(semanticModel, token, cancellationToken);

            foreach (var braceMatcher in braceMatchers)
            {
                // If this service added values then need to check the other ones.
                var result = braceMatcher.Value.FindBraces(document.Project, semanticModel, token, position, options, cancellationToken);
                if (result.HasValue)
                {
                    return(result);
                }
            }

            return(null);
        }
        public BraceMatchingResult?FindBraces(
            Project project,
            SemanticModel semanticModel,
            SyntaxToken token,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            if (!options.HighlightingOptions.HighlightRelatedJsonComponentsUnderCursor)
            {
                return(null);
            }

            var info     = project.GetRequiredLanguageService <IEmbeddedLanguagesProvider>().EmbeddedLanguageInfo;
            var detector = JsonLanguageDetector.GetOrCreate(semanticModel.Compilation, info);

            // We do support brace matching in strings that look very likely to be json, even if we aren't 100% certain
            // if it truly is json.
            var tree = detector.TryParseString(token, semanticModel, includeProbableStrings: true, cancellationToken);

            if (tree == null)
            {
                return(null);
            }

            return(GetMatchingBraces(tree, position));
        }
Example #6
0
        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);
        }
        private static async Task <(BraceMatchingResult?leftOfPosition, BraceMatchingResult?rightOfPosition)> GetAllMatchingBracesAsync(
            IBraceMatchingService service,
            Document document,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            // These are the matching spans when checking the token to the right of the position.
            var rightOfPosition = await service.GetMatchingBracesAsync(document, position, options, cancellationToken).ConfigureAwait(false);

            // The braces to the right of the position should only be added if the position is
            // actually within the span of the start brace.  Note that this is what we want for
            // single character braces as well as multi char braces.  i.e. if the user has:
            //
            //      ^{ }    // then { and } are matching braces.
            //      {^ }    // then { and } are not matching braces.
            //
            //      ^<@ @>  // then <@ and @> are matching braces.
            //      <^@ @>  // then <@ and @> are matching braces.
            //      <@^ @>  // then <@ and @> are not matching braces.
            if (rightOfPosition.HasValue &&
                !rightOfPosition.Value.LeftSpan.Contains(position))
            {
                // Not a valid match.
                rightOfPosition = null;
            }

            if (position == 0)
            {
                // We're at the start of the document, can't find braces to the left of the position.
                return(leftOfPosition : null, rightOfPosition);
            }

            // See if we're touching the end of some construct.  i.e.:
            //
            //      { }^
            //      <@ @>^
            //      <@ @^>
            //
            // But not
            //
            //      { ^}
            //      <@ ^@>

            var leftOfPosition = await service.GetMatchingBracesAsync(document, position - 1, options, cancellationToken).ConfigureAwait(false);

            if (leftOfPosition.HasValue &&
                position <= leftOfPosition.Value.RightSpan.End &&
                position > leftOfPosition.Value.RightSpan.Start)
            {
                // Found a valid pair on the left of us.
                return(leftOfPosition, rightOfPosition);
            }

            // No valid pair of braces on the left of us.
            return(leftOfPosition : null, rightOfPosition);
        }
            public async Task <BraceMatchingResult?> GetMatchingBracesAsync(
                Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
            {
                var text   = (await document.GetTextAsync(cancellationToken)).ToString();
                var braces = GetMatchingBraces(text, position);

                if (braces.HasValue)
                {
                    Debug.Assert(text.Substring(braces.Value.LeftSpan.Start, braces.Value.LeftSpan.Length) == "<@");
                    Debug.Assert(text.Substring(braces.Value.RightSpan.Start, braces.Value.RightSpan.Length) == "@>");
                }

                return(braces);
            }
        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));
        }
        public static async Task <TextSpan?> FindMatchingSpanAsync(
            this IBraceMatchingService service,
            Document document,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            // These are the matching spans when checking the token to the right of the position.
            var braces1 = await service.GetMatchingBracesAsync(document, position, options, cancellationToken).ConfigureAwait(false);

            // These are the matching spans when checking the token to the left of the position.
            BraceMatchingResult?braces2 = null;

            // Ensure caret is valid at left of position.
            if (position > 0)
            {
                braces2 = await service.GetMatchingBracesAsync(document, position - 1, options, cancellationToken).ConfigureAwait(false);
            }

            // Favor matches where the position is on the outside boundary of the braces. i.e. if we
            // have:  {^()}
            //
            // then this would return the  ()  not the  {}
            if (braces1.HasValue && position >= braces1.Value.LeftSpan.Start && position < braces1.Value.LeftSpan.End)
            {
                // ^{ } -- return right span
                return(braces1.Value.RightSpan);
            }
            else if (braces2.HasValue && position > braces2.Value.RightSpan.Start && position <= braces2.Value.RightSpan.End)
            {
                // { }^ -- return left span
                return(braces2.Value.LeftSpan);
            }
            else if (braces2.HasValue && position > braces2.Value.LeftSpan.Start && position <= braces2.Value.LeftSpan.End)
            {
                // {^ } -- return right span
                return(braces2.Value.RightSpan);
            }
            else if (braces1.HasValue && position >= braces1.Value.RightSpan.Start && position < braces1.Value.RightSpan.End)
            {
                // { ^} - return left span
                return(braces1.Value.LeftSpan);
            }

            return(null);
        }
        public BraceMatchingResult?FindBraces(
            Project project,
            SemanticModel semanticModel,
            SyntaxToken token,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            if (!options.HighlightingOptions.HighlightRelatedRegexComponentsUnderCursor)
            {
                return(null);
            }

            var info     = project.GetRequiredLanguageService <IEmbeddedLanguagesProvider>().EmbeddedLanguageInfo;
            var detector = RegexLanguageDetector.GetOrCreate(semanticModel.Compilation, info);
            var tree     = detector.TryParseString(token, semanticModel, cancellationToken);

            return(tree == null ? null : GetMatchingBraces(tree, position));
        }
Example #12
0
        public BraceMatchingResult?FindBraces(
            Project project,
            SemanticModel semanticModel,
            SyntaxToken token,
            int position,
            BraceMatchingOptions options,
            CancellationToken cancellationToken)
        {
            var braceMatchers = AspNetCoreBraceMatcherExtensionProvider.GetExtensions(project);

            foreach (var braceMatcher in braceMatchers)
            {
                var result = braceMatcher.FindBraces(semanticModel, token, position, cancellationToken)?.ToBraceMatchingResult();
                if (result != null)
                {
                    return(result);
                }
            }

            return(null);
        }
Example #13
0
        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);
                }
            }
        }
        public async Task <BraceMatchingResult?> FindBracesAsync(Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position);

            if (!token.ContainsDiagnostics)
            {
                if (token.IsKind(SyntaxKind.StringLiteralToken))
                {
                    return(GetSimpleStringBraceMatchingResult(token, endTokenLength: 1));
                }
                else if (token.IsKind(SyntaxKind.Utf8StringLiteralToken))
                {
                    return(GetSimpleStringBraceMatchingResult(token, endTokenLength: 3));
                }
                else if (token.IsKind(SyntaxKind.InterpolatedStringStartToken, SyntaxKind.InterpolatedVerbatimStringStartToken))
                {
                    if (token.Parent is InterpolatedStringExpressionSyntax interpolatedString)
                    {
                        return(new BraceMatchingResult(token.Span, interpolatedString.StringEndToken.Span));
                    }
                }
                else if (token.IsKind(SyntaxKind.InterpolatedStringEndToken))
                {
                    if (token.Parent is InterpolatedStringExpressionSyntax interpolatedString)
                    {
                        return(new BraceMatchingResult(interpolatedString.StringStartToken.Span, token.Span));
                    }
                }
            }

            return(null);
        }
        internal async Task ProduceTagsAsync(
            TaggerContext <BraceHighlightTag> context, Document document, ITextSnapshot snapshot, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Tagger_BraceHighlighting_TagProducer_ProduceTags, cancellationToken))
            {
                if (position >= 0 && position <= snapshot.Length)
                {
                    var(bracesLeftOfPosition, bracesRightOfPosition) = await GetAllMatchingBracesAsync(
                        _braceMatcherService, document, position, options, cancellationToken).ConfigureAwait(false);

                    AddBraces(context, snapshot, bracesLeftOfPosition);
                    AddBraces(context, snapshot, bracesRightOfPosition);
                }
            }
        }
Example #16
0
        public async Task <BraceMatchingResult?> FindBracesAsync(Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var token = root.FindToken(position, findInsideTrivia: true);

            if (token.Parent is not TDirectiveTriviaSyntax directive)
            {
                return(null);
            }

            TDirectiveTriviaSyntax matchingDirective = null;

            if (IsConditionalDirective(directive))
            {
                // #if/#elif/#else/#endif directive cases.
                var matchingDirectives = GetMatchingConditionalDirectives(directive, cancellationToken);
                if (matchingDirectives?.Count > 0)
                {
                    matchingDirective = matchingDirectives[(matchingDirectives.IndexOf(directive) + 1) % matchingDirectives.Count];
                }
            }
            else
            {
                // #region/#endregion or other directive cases.
                matchingDirective = GetMatchingDirective(directive, cancellationToken);
            }

            if (matchingDirective == null)
            {
                // one line directives, that do not have a matching begin/end directive pair.
                return(null);
            }

            return(new BraceMatchingResult(
                       LeftSpan: GetSpanForTagging(directive),
                       RightSpan: GetSpanForTagging(matchingDirective)));
        }
Example #17
0
        public async Task <BraceMatchingResult?> GetMatchingBracesAsync(Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            if (position < 0 || position > text.Length)
            {
                throw new ArgumentException(nameof(position));
            }

            var matchers = _braceMatchers.Where(b => b.Metadata.Language == document.Project.Language);

            foreach (var matcher in matchers)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var braces = await matcher.Value.FindBracesAsync(document, position, options, cancellationToken).ConfigureAwait(false);

                if (braces.HasValue)
                {
                    return(braces);
                }
            }

            return(null);
        }
Example #18
0
 Task <BraceMatchingResult?> IBraceMatcher.FindBracesAsync(Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
 => FindBracesAsync(document, position, cancellationToken);
        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);
        }
Example #20
0
        public async Task <BraceMatchingResult?> FindBracesAsync(Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken)
        {
            var result = await _impl.FindBracesAsync(document, position, cancellationToken).ConfigureAwait(false);

            return(result.HasValue ? new BraceMatchingResult(result.Value.LeftSpan, result.Value.RightSpan) : null);
        }