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); }
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); }
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)); }
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)); }
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); }
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); } } }
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))); }
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); }
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); }
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); }