public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { Document document = context.Document; Microsoft.CodeAnalysis.Text.TextSpan textSpan = context.Span; CancellationToken cancellationToken = context.CancellationToken; CompilationUnitSyntax root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false) as CompilationUnitSyntax; SemanticModel model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // if length is 0 then no particular range is selected, so pick the first enclosing member if (textSpan.Length == 0) { MemberDeclarationSyntax decl = root.FindToken(textSpan.Start).Parent.AncestorsAndSelf().OfType <MemberDeclarationSyntax>().FirstOrDefault(); if (decl != null) { textSpan = decl.FullSpan; } } IEnumerable <ExpandablePropertyInfo> properties = ExpansionChecker.GetExpandableProperties(textSpan, root, model); if (properties.Any()) { CodeAction action = CodeAction.Create( "Apply INotifyPropertyChanged pattern", c => ImplementNotifyPropertyChangedAsync(document, root, model, properties, c), equivalenceKey: nameof(ImplementNotifyPropertyChangedCodeRefactoringProvider)); context.RegisterRefactoring(action); } }
public static bool ShouldUseSmartTokenFormatterInsteadOfIndenter( IEnumerable <IFormattingRule> formattingRules, CompilationUnitSyntax root, TextLine line, OptionSet optionSet, CancellationToken cancellationToken) { Contract.ThrowIfNull(formattingRules); Contract.ThrowIfNull(root); //if (!optionSet.GetOption(FeatureOnOffOptions.AutoFormattingOnReturn, LanguageNames.CSharp)) //{ // return false; //} if (optionSet.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp) != FormattingOptions.IndentStyle.Smart) { return(false); } var firstNonWhitespacePosition = line.GetFirstNonWhitespacePosition(); if (!firstNonWhitespacePosition.HasValue) { return(false); } var token = root.FindToken(firstNonWhitespacePosition.Value); if (token.IsKind(SyntaxKind.None) || token.SpanStart != firstNonWhitespacePosition) { return(false); } // first see whether there is a line operation for current token var previousToken = token.GetPreviousToken(includeZeroWidth: true); // only use smart token formatter when we have two visible tokens. if (previousToken.Kind() == SyntaxKind.None || previousToken.IsMissing) { return(false); } var lineOperation = FormattingOperations.GetAdjustNewLinesOperation(formattingRules, previousToken, token, optionSet); if (lineOperation == null || lineOperation.Option == AdjustNewLinesOption.ForceLinesIfOnSingleLine) { // no indentation operation, nothing to do for smart token formatter return(false); } // We're pressing enter between two tokens, have the formatter figure out hte appropriate // indentation. return(true); }
public static bool ShouldUseSmartTokenFormatterInsteadOfIndenter( IEnumerable <IFormattingRule> formattingRules, CompilationUnitSyntax root, ITextSnapshotLine line, OptionSet optionSet, CancellationToken cancellationToken) { Contract.ThrowIfNull(formattingRules); Contract.ThrowIfNull(root); Contract.ThrowIfNull(line); if (optionSet.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp) != FormattingOptions.IndentStyle.Smart) { return(false); } var firstNonWhitespacePosition = line.GetFirstNonWhitespacePosition(); if (!firstNonWhitespacePosition.HasValue) { return(false); } var token = root.FindToken(firstNonWhitespacePosition.Value); if (token.IsKind(SyntaxKind.None) || token.SpanStart != firstNonWhitespacePosition) { return(false); } // first see whether there is a line operation for current token var previousToken = token.GetPreviousToken(includeZeroWidth: true); // only use smart token formatter when we have two visible tokens. if (previousToken.Kind() == SyntaxKind.None || previousToken.IsMissing) { return(false); } var lineOperation = FormattingOperations.GetAdjustNewLinesOperation(formattingRules, previousToken, token, optionSet); if (lineOperation != null) { return(true); } // no indentation operation, nothing to do for smart token formatter return(false); }
private static void Analyze(SyntaxNodeAnalysisContext context) { CompilationUnitSyntax node = (CompilationUnitSyntax)context.Node; if (Helper.IsGeneratedCode(context) || Helper.IsAssemblyInfo(context) || Helper.HasAutoGeneratedComment(node)) { return; } if (node.FindToken(0).IsKind(SyntaxKind.EndOfFileToken)) { return; } var first = node.GetLeadingTrivia(); if (!first.Any()) { CreateDiagnostic(context, node.GetLocation()); return; } SyntaxTrivia copyrightSyntax = first[0]; if (first[0].IsKind(SyntaxKind.RegionDirectiveTrivia)) { if (first.Count < 2 || !first[1].IsKind(SyntaxKind.SingleLineCommentTrivia)) { CreateDiagnostic(context, context.Node.GetLocation()); return; } copyrightSyntax = first[1]; } bool isCorrectStatement = CheckCopyrightStatement(context, copyrightSyntax); if (!isCorrectStatement) { CreateDiagnostic(context, copyrightSyntax.GetLocation()); return; } }
/// <summary> /// Checks for the presence of an "autogenerated" comment in the starting trivia for a file /// The compiler generates a version of the AssemblyInfo.cs file for certain projects (not named AssemblyInfo.cs), and this is how to pick it up /// </summary> /// <param name="node"></param> /// <returns></returns> public static bool HasAutoGeneratedComment(CompilationUnitSyntax node) { if (node.FindToken(0).IsKind(SyntaxKind.EndOfFileToken)) { return(false); } var first = node.GetLeadingTrivia(); if (first.Count == 0) { return(false); } string possibleHeader = first.ToFullString(); bool isAutogenerated = possibleHeader.Contains(@"<autogenerated />") || possibleHeader.Contains("<auto-generated"); return(isAutogenerated); }
bool UpdateSemanticModel() { try { _Document = View.TextSnapshot.GetOpenDocumentInCurrentContextWithChanges(); _SemanticModel = _Document.GetSemanticModelAsync().Result; _Compilation = _SemanticModel.SyntaxTree.GetCompilationUnitRoot(); } catch (NullReferenceException) { _Node = null; _Token = default; _Trivia = default; _LineComment = default; return(false); } int pos = View.Selection.Start.Position; try { _Token = _Compilation.FindToken(pos, true); } catch (ArgumentOutOfRangeException) { _Node = null; _Token = default; _Trivia = default; _LineComment = default; return(false); } var triviaList = _Token.HasLeadingTrivia ? _Token.LeadingTrivia : _Token.HasTrailingTrivia ? _Token.TrailingTrivia : default; if (triviaList.Equals(SyntaxTriviaList.Empty) == false && triviaList.FullSpan.Contains(pos)) { _Trivia = triviaList.FirstOrDefault(i => i.Span.Contains(pos)); _LineComment = triviaList.FirstOrDefault(i => i.IsLineComment()); } else { _Trivia = _LineComment = default; } _Node = _Compilation.FindNode(_Token.Span, true, true); return(true); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { Document document = context.Document; Microsoft.CodeAnalysis.Text.TextSpan textSpan = context.Span; CancellationToken cancellationToken = context.CancellationToken; CompilationUnitSyntax root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SyntaxToken token = root.FindToken(textSpan.Start); SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (token.Kind() == SyntaxKind.IdentifierToken && token.Parent.Kind() == SyntaxKind.ClassDeclaration) { string className = token.Text; CreateEqualityCompareCodeAction action = new CreateEqualityCompareCodeAction($"Create EqualityCompare for {className}", (c) => Task.FromResult(CreateEqualityCompareConverterForClass(document, semanticModel, token, root, c))); context.RegisterRefactoring(action); } }
static DebugDataTipInfo GetInfo(CompilationUnitSyntax root, SemanticModel semanticModel, int position, CancellationToken cancellationToken) { var token = root.FindToken(position); string textOpt = null; var expression = token.Parent as ExpressionSyntax; if (expression == null) { if (Microsoft.CodeAnalysis.CSharpExtensions.IsKind(token, SyntaxKind.IdentifierToken)) { if (token.Parent is MethodDeclarationSyntax) { return(default(DebugDataTipInfo)); } if (semanticModel != null) { if (token.Parent is PropertyDeclarationSyntax) { var propertySymbol = semanticModel.GetDeclaredSymbol((PropertyDeclarationSyntax)token.Parent); if (propertySymbol.IsStatic) { textOpt = propertySymbol.ContainingType.GetFullName() + "." + propertySymbol.Name; } } else if (token.GetAncestor <FieldDeclarationSyntax> () != null) { var fieldSymbol = semanticModel.GetDeclaredSymbol(token.GetAncestor <VariableDeclaratorSyntax> ()); if (fieldSymbol.IsStatic) { textOpt = fieldSymbol.ContainingType.GetFullName() + "." + fieldSymbol.Name; } } } return(new DebugDataTipInfo(token.Span, text: textOpt)); } else { return(default(DebugDataTipInfo)); } } if (expression.IsAnyLiteralExpression()) { // If the user hovers over a literal, give them a DataTip for the type of the // literal they're hovering over. // Partial semantics should always be sufficient because the (unconverted) type // of a literal can always easily be determined. var type = semanticModel?.GetTypeInfo(expression, cancellationToken).Type; return(type == null ? default(DebugDataTipInfo) : new DebugDataTipInfo(expression.Span, type.GetFullName())); } // Check if we are invoking method and if we do return null so we don't invoke it if (expression.Parent is InvocationExpressionSyntax || (semanticModel != null && expression.Parent is MemberAccessExpressionSyntax && expression.Parent.Parent is InvocationExpressionSyntax && semanticModel.GetSymbolInfo(token).Symbol is IMethodSymbol)) { return(default(DebugDataTipInfo)); } if (expression.IsRightSideOfDotOrArrow()) { var curr = expression; while (true) { var conditionalAccess = curr.GetParentConditionalAccessExpression(); if (conditionalAccess == null) { break; } curr = conditionalAccess; } if (curr == expression) { // NB: Parent.Span, not Span as below. return(new DebugDataTipInfo(expression.Parent.Span, text: null)); } // NOTE: There may not be an ExpressionSyntax corresponding to the range we want. // For example, for input a?.$$B?.C, we want span [|a?.B|]?.C. return(new DebugDataTipInfo(TextSpan.FromBounds(curr.SpanStart, expression.Span.End), text: null)); } var typeSyntax = expression as TypeSyntax; if (typeSyntax != null && typeSyntax.IsVar) { // If the user is hovering over 'var', then pass back the full type name that 'var' // binds to. var type = semanticModel?.GetTypeInfo(typeSyntax, cancellationToken).Type; if (type != null) { textOpt = type.GetFullName(); } } if (semanticModel != null) { if (expression is IdentifierNameSyntax) { if (expression.Parent is ObjectCreationExpressionSyntax) { textOpt = ((INamedTypeSymbol)semanticModel.GetSymbolInfo(expression).Symbol).GetFullName(); } else if (expression.Parent is AssignmentExpressionSyntax && expression.Parent.Parent is InitializerExpressionSyntax) { var variable = expression.GetAncestor <VariableDeclaratorSyntax> (); if (variable != null) { textOpt = variable.Identifier.Text + "." + ((IdentifierNameSyntax)expression).Identifier.Text; } } } } return(new DebugDataTipInfo(expression.Span, textOpt)); }
//static int s_test; public static void AddSnippets(List <CiComplItem> items, TextSpan span, CompilationUnitSyntax root, string code, CSharpSyntaxContext syncon) { //CSharpSyntaxContext was discovered later and therefore almost not used here. if (syncon.IsObjectCreationTypeContext) { return; } //CiUtil.GetContextType(syncon); //print.clear(); print.it(++s_test); //print.clear(); //foreach (var v in root.ChildNodes()) { // CiUtil.PrintNode(v); //} //print.it("---"); _Context context = _Context.Unknown; int pos = span.Start; //get node from start var token = root.FindToken(pos); var node = token.Parent; //CiUtil.PrintNode(node); //print.it("--"); //return; //find ancestor/self that contains pos inside while (node != null && !node.Span.ContainsInside(pos)) { node = node.Parent; } //CiUtil.PrintNode(node); //for(var v = node; v != null; v = v.Parent) print.it(v.GetType().Name, v is ExpressionSyntax, v is ExpressionStatementSyntax); //print.it(SyntaxFacts.IsTopLevelStatement); //print.it(SyntaxFacts.IsInNamespaceOrTypeContext); //not tested switch (node) { case BlockSyntax: case SwitchSectionSyntax: //between case: and break; case ElseClauseSyntax: case LabeledStatementSyntax: case IfStatementSyntax s1 when pos > s1.CloseParenToken.SpanStart: case WhileStatementSyntax s2 when pos > s2.CloseParenToken.SpanStart: case DoStatementSyntax s3 when pos <s3.WhileKeyword.SpanStart: case ForStatementSyntax s4 when pos> s4.CloseParenToken.SpanStart: case CommonForEachStatementSyntax s5 when pos > s5.CloseParenToken.SpanStart: case LockStatementSyntax s6 when pos > s6.CloseParenToken.SpanStart: case FixedStatementSyntax s7 when pos > s7.CloseParenToken.SpanStart: case UsingStatementSyntax s8 when pos > s8.CloseParenToken.SpanStart: context = _Context.Function; break; case TypeDeclarationSyntax td when pos > td.OpenBraceToken.Span.Start: //{ } of class, struct, interface context = _Context.Type; break; case NamespaceDeclarationSyntax ns when pos > ns.OpenBraceToken.Span.Start: case CompilationUnitSyntax: case null: context = _Context.Namespace | _Context.Function; //Function for C# 9 top-level statements. //FUTURE: only if in correct place. break; case LambdaExpressionSyntax: case ArrowExpressionClauseSyntax: //like void F() =>here context = _Context.Arrow; break; case AttributeListSyntax: context = _Context.Attributes; break; default: if (span.IsEmpty) //if '=> here;' or '=> here)' etc, use => { var t2 = token.GetPreviousToken(); if (t2.IsKind(SyntaxKind.EqualsGreaterThanToken) && t2.Parent is LambdaExpressionSyntax) { context = _Context.Arrow; } } break; } //print.it(context); s_context = context; if (s_items == null) { var a = new List <_CiComplItemSnippet>(); if (!filesystem.exists(CustomFile).File) { try { filesystem.copy(folders.ThisAppBS + @"Default\Snippets2.xml", CustomFile); } catch { goto g1; } } _LoadFile(CustomFile, true); g1 : _LoadFile(DefaultFile, false); if (a.Count == 0) { return; } s_items = a; void _LoadFile(string file, bool custom) { try { var xroot = XmlUtil.LoadElem(file); foreach (var xg in xroot.Elements("group")) { if (!xg.Attr(out string sc, "context")) { continue; } _Context con = default; if (sc == "Function") { con = _Context.Function; //many } else //few, eg Type or Namespace|Type { foreach (var seg in sc.Segments("|")) { switch (sc[seg.Range]) { case "Function": con |= _Context.Function; break; case "Type": con |= _Context.Type; break; case "Namespace": con |= _Context.Namespace; break; case "Arrow": con |= _Context.Arrow; break; case "Attributes": con |= _Context.Attributes; break; case "Any": con |= _Context.Any; break; case "Line": con |= _Context.Line; break; } } } if (con == default) { continue; } foreach (var xs in xg.Elements("snippet")) { a.Add(new _CiComplItemSnippet(xs.Attr("name"), xs, con, custom)); } } } catch (Exception ex) { print.it("Failed to load snippets from " + file + "\r\n\t" + ex.ToStringWithoutStack()); } } //FUTURE: support $selection$. Add menu Edit -> Surround -> Snippet1|Snippet2|.... //FUTURE: snippet editor, maybe like in Eclipse. } bool isLineStart = InsertCodeUtil.IsLineStart(code, pos); foreach (var v in s_items) { if (!v.context.HasAny(context)) { continue; } if (v.context.Has(_Context.Line) && !isLineStart) { continue; } v.group = 0; v.hidden = 0; v.hilite = 0; v.moveDown = 0; v.ci.Span = span; items.Add(v); } }