public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var fromNodes = await context .GetRelevantNodesAsync <TFromExpression>() .ConfigureAwait(false); var from = fromNodes.FirstOrDefault(n => n.RawKind == FromKind); if (from == null) { return; } if (from.GetDiagnostics().Any(d => d.DefaultSeverity == DiagnosticSeverity.Error)) { return; } var(document, _, cancellationToken) = context; var typeNode = GetTypeNode(from); var semanticModel = await document .GetRequiredSemanticModelAsync(cancellationToken) .ConfigureAwait(false); var type = semanticModel.GetTypeInfo(typeNode, cancellationToken).Type; if (type is { TypeKind : not TypeKind.Error, IsReferenceType : true })
protected override async Task <SyntaxNode?> GetSelectedClassDeclarationAsync( CodeRefactoringContext context ) { var relaventNodes = await context .GetRelevantNodesAsync <ClassDeclarationSyntax>() .ConfigureAwait(false); return(relaventNodes.FirstOrDefault()); }
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, span, cancellationToken) = context; var root = await document .GetRequiredSyntaxRootAsync(cancellationToken) .ConfigureAwait(false); var node = root.FindNode(span); if (!IsInAsyncContext(node)) { return; } var model = await document .GetRequiredSemanticModelAsync(cancellationToken) .ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var expressions = await context .GetRelevantNodesAsync <TExpressionSyntax>() .ConfigureAwait(false); for (var i = expressions.Length - 1; i >= 0; i--) { var expression = expressions[i]; if (IsValidAwaitableExpression(expression, model, syntaxFacts)) { context.RegisterRefactoring( new MyCodeAction( GetTitle(), c => AddAwaitAsync(document, expression, withConfigureAwait: false, c) ), expression.Span ); context.RegisterRefactoring( new MyCodeAction( GetTitleWithConfigureAwait(), c => AddAwaitAsync(document, expression, withConfigureAwait: true, c) ), expression.Span ); } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, textSpan, cancellationToken) = context; var possibleExpressions = await context.GetRelevantNodesAsync <TExpressionSyntax>().ConfigureAwait(false); var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // let's take the largest (last) StringConcat we can given current textSpan var top = possibleExpressions .Where(expr => IsStringConcat(syntaxFacts, expr, semanticModel, cancellationToken)) .LastOrDefault(); if (top == null) { return; } // Currently we can concatenate only full subtrees. Therefore we can't support arbitrary selection. We could // theoretically support selecting the selections that correspond to full sub-trees (e.g. prefixes of // correct length but from UX point of view that it would feel arbitrary). // Thus, we only support selection that takes the whole topmost expression. It breaks some leniency around under-selection // but it's the best solution so far. if (CodeRefactoringHelpers.IsNodeUnderselected(top, textSpan) || IsStringConcat(syntaxFacts, top.Parent, semanticModel, cancellationToken)) { return; } // Now walk down the concatenation collecting all the pieces that we are // concatenating. var pieces = new List <SyntaxNode>(); CollectPiecesDown(syntaxFacts, pieces, top, semanticModel, cancellationToken); var stringLiterals = pieces .Where(x => syntaxFacts.IsStringLiteralExpression(x) || syntaxFacts.IsCharacterLiteralExpression(x)) .ToImmutableArray(); // If the entire expression is just concatenated strings, then don't offer to // make an interpolated string. The user likely manually split this for // readability. if (stringLiterals.Length == pieces.Count) { return; } var isVerbatimStringLiteral = false; if (stringLiterals.Length > 0) { // Make sure that all the string tokens we're concatenating are the same type // of string literal. i.e. if we have an expression like: @" "" " + " \r\n " // then we don't merge this. We don't want to be munging different types of // escape sequences in these strings, so we only support combining the string // tokens if they're all the same type. var firstStringToken = stringLiterals[0].GetFirstToken(); isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken); if (stringLiterals.Any( lit => isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(lit.GetFirstToken()))) { return; } } var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var interpolatedString = CreateInterpolatedString(document, isVerbatimStringLiteral, pieces); context.RegisterRefactoring( new MyCodeAction( _ => UpdateDocumentAsync(document, root, top, interpolatedString)), top.Span); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var(document, textSpan, cancellationToken) = context; var possibleExpressions = await context.GetRelevantNodesAsync <TExpressionSyntax>().ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); // let's take the largest (last) StringConcat we can given current textSpan var top = possibleExpressions .Where(expr => IsStringConcat(syntaxFacts, expr, semanticModel, cancellationToken)) .LastOrDefault(); if (top == null) { return; } if (!syntaxFacts.SupportsConstantInterpolatedStrings(document.Project.ParseOptions !)) { // if there is a const keyword, the refactoring shouldn't show because interpolated string is not const string var declarator = top.FirstAncestorOrSelf <SyntaxNode>(syntaxFacts.IsVariableDeclarator); if (declarator != null) { var generator = SyntaxGenerator.GetGenerator(document); if (generator.GetModifiers(declarator).IsConst) { return; } } } // Currently we can concatenate only full subtrees. Therefore we can't support arbitrary selection. We could // theoretically support selecting the selections that correspond to full sub-trees (e.g. prefixes of // correct length but from UX point of view that it would feel arbitrary). // Thus, we only support selection that takes the whole topmost expression. It breaks some leniency around under-selection // but it's the best solution so far. if (CodeRefactoringHelpers.IsNodeUnderselected(top, textSpan) || IsStringConcat(syntaxFacts, top.Parent, semanticModel, cancellationToken)) { return; } // Now walk down the concatenation collecting all the pieces that we are // concatenating. using var _ = ArrayBuilder <SyntaxNode> .GetInstance(out var pieces); CollectPiecesDown(syntaxFacts, pieces, top, semanticModel, cancellationToken); var stringLiterals = pieces .Where(x => syntaxFacts.IsStringLiteralExpression(x) || syntaxFacts.IsCharacterLiteralExpression(x)) .ToImmutableArray(); // If the entire expression is just concatenated strings, then don't offer to // make an interpolated string. The user likely manually split this for // readability. if (stringLiterals.Length == pieces.Count) { return; } var isVerbatimStringLiteral = false; if (stringLiterals.Length > 0) { // Make sure that all the string tokens we're concatenating are the same type // of string literal. i.e. if we have an expression like: @" "" " + " \r\n " // then we don't merge this. We don't want to be munging different types of // escape sequences in these strings, so we only support combining the string // tokens if they're all the same type. var firstStringToken = stringLiterals[0].GetFirstToken(); isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken); if (stringLiterals.Any(