public async Task<bool> IsValidAsync( Document document, int position, string expressionValue, CancellationToken cancellationToken) { var expression = SyntaxFactory.ParseExpression(expressionValue); var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); if (token.Kind() == SyntaxKind.CloseBraceToken && token.GetPreviousToken().Kind() != SyntaxKind.None) { token = token.GetPreviousToken(); } var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var info = semanticModel.GetSpeculativeSymbolInfo(token.SpanStart, expression, SpeculativeBindingOption.BindAsExpression); if (info.Symbol == null) { return false; } // We seem to have bound successfully. However, if it bound to a local, then make // sure that that local isn't after the statement that we're currently looking at. if (info.Symbol.Kind == SymbolKind.Local) { var statement = info.Symbol.Locations.First().FindToken(cancellationToken).GetAncestor<StatementSyntax>(); if (statement != null && position < statement.SpanStart) { return false; } } return true; }
protected override bool TryGetSimplifiedTypeName(Document documentWithFullyQualifiedTypeName, TextSpan updatedTextSpan, CancellationToken cancellationToken, out string simplifiedTypeName) { simplifiedTypeName = string.Empty; var typeAnnotation = new SyntaxAnnotation(); var syntaxRoot = documentWithFullyQualifiedTypeName.GetCSharpSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken); var nodeToReplace = syntaxRoot.DescendantNodes().FirstOrDefault(n => n.Span == updatedTextSpan); if (nodeToReplace == null) { return false; } var updatedRoot = syntaxRoot.ReplaceNode(nodeToReplace, nodeToReplace.WithAdditionalAnnotations(typeAnnotation, Simplifier.Annotation)); var documentWithAnnotations = documentWithFullyQualifiedTypeName.WithSyntaxRoot(updatedRoot); var simplifiedDocument = Simplifier.ReduceAsync(documentWithAnnotations, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); simplifiedTypeName = simplifiedDocument.GetCSharpSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken).GetAnnotatedNodesAndTokens(typeAnnotation).Single().ToString(); return true; }
protected override async Task<SignatureHelpItems> GetItemsWorkerAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, CancellationToken cancellationToken) { var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false); ExpressionSyntax expression; SyntaxToken openBrace; if (!TryGetElementAccessExpression(root, position, document.GetLanguageService<ISyntaxFactsService>(), triggerInfo.TriggerReason, cancellationToken, out expression, out openBrace)) { return null; } var semanticModel = await document.GetCSharpSemanticModelAsync(cancellationToken).ConfigureAwait(false); var expressionSymbol = semanticModel.GetSymbolInfo(expression, cancellationToken).GetAnySymbol(); if (expressionSymbol is INamedTypeSymbol) { // foo?[$$] var namedType = (INamedTypeSymbol)expressionSymbol; if (namedType.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T && expression.IsKind(SyntaxKind.NullableType) && expression.IsChildNode<ArrayTypeSyntax>(a => a.ElementType)) { // Speculatively bind the type part of the nullable as an expression var nullableTypeSyntax = (NullableTypeSyntax)expression; var speculativeBinding = semanticModel.GetSpeculativeSymbolInfo(position, nullableTypeSyntax.ElementType, SpeculativeBindingOption.BindAsExpression); expressionSymbol = speculativeBinding.GetAnySymbol(); expression = nullableTypeSyntax.ElementType; } } if (expressionSymbol != null && expressionSymbol is INamedTypeSymbol) { return null; } IEnumerable<IPropertySymbol> indexers; ITypeSymbol expressionType; if (!TryGetIndexers(position, semanticModel, expression, cancellationToken, out indexers, out expressionType) && !TryGetComIndexers(semanticModel, expression, cancellationToken, out indexers, out expressionType)) { return null; } var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken); if (within == null) { return null; } var accessibleIndexers = indexers.Where(m => m.IsAccessibleWithin(within, throughTypeOpt: expressionType)); if (!accessibleIndexers.Any()) { return null; } var symbolDisplayService = document.Project.LanguageServices.GetService<ISymbolDisplayService>(); accessibleIndexers = accessibleIndexers.FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation) .Sort(symbolDisplayService, semanticModel, expression.SpanStart); var anonymousTypeDisplayService = document.Project.LanguageServices.GetService<IAnonymousTypeDisplayService>(); var documentationCommentFormattingService = document.Project.LanguageServices.GetService<IDocumentationCommentFormattingService>(); var textSpan = GetTextSpan(expression, openBrace); var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); return CreateSignatureHelpItems(accessibleIndexers.Select(p => Convert(p, openBrace, semanticModel, symbolDisplayService, anonymousTypeDisplayService, documentationCommentFormattingService, cancellationToken)), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken)); }
private async Task<bool?> IsTriggerOnDotAsync(Document document, int characterPosition, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (text[characterPosition] != '.') { return null; } // don't want to trigger after a number. All other cases after dot are ok. var tree = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = tree.FindToken(characterPosition); if (token.Kind() == SyntaxKind.DotToken) { token = token.GetPreviousToken(); } return token.Kind() != SyntaxKind.NumericLiteralToken; }
internal static async Task<DebugDataTipInfo> GetInfoAsync(Document document, int position, CancellationToken cancellationToken) { var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); var expression = token.Parent as ExpressionSyntax; if (expression == null) { return token.IsKind(SyntaxKind.IdentifierToken) ? new DebugDataTipInfo(token.Span, text: null) : 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 partialDocument = await document.WithFrozenPartialSemanticsAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await partialDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; return type == null ? default(DebugDataTipInfo) : new DebugDataTipInfo(expression.Span, type.ToNameDisplayString()); } 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); } // NOTE(cyrusn): This behavior is to mimic what we did in Dev10, i'm not sure if it's // necessary or not. if (expression.IsKind(SyntaxKind.InvocationExpression)) { expression = ((InvocationExpressionSyntax)expression).Expression; } string textOpt = 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 semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var type = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type; if (type != null) { textOpt = type.ToNameDisplayString(); } } return new DebugDataTipInfo(expression.Span, textOpt); }
internal override Document AddImports(Document document, XElement snippetNode, bool placeSystemNamespaceFirst, CancellationToken cancellationToken) { var importsNode = snippetNode.Element(XName.Get("Imports", snippetNode.Name.NamespaceName)); if (importsNode == null || !importsNode.HasElements) { return document; } var newUsingDirectives = GetUsingDirectivesToAdd(document, snippetNode, importsNode, cancellationToken); if (!newUsingDirectives.Any()) { return document; } // In Venus/Razor, inserting imports statements into the subject buffer does not work. // Instead, we add the imports through the contained language host. if (TryAddImportsToContainedDocument(document, newUsingDirectives.Where(u => u.Alias == null).Select(u => u.Name.ToString()))) { return document; } var root = document.GetCSharpSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken); var newRoot = root.AddUsingDirectives(newUsingDirectives, placeSystemNamespaceFirst); var newDocument = document.WithSyntaxRoot(newRoot); var formattedDocument = Formatter.FormatAsync(newDocument, Formatter.Annotation, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); document.Project.Solution.Workspace.ApplyDocumentChanges(formattedDocument, cancellationToken); return formattedDocument; }
private static IList<UsingDirectiveSyntax> GetUsingDirectivesToAdd(Document document, XElement snippetNode, XElement importsNode, CancellationToken cancellationToken) { var namespaceXmlName = XName.Get("Namespace", snippetNode.Name.NamespaceName); var existingUsings = document.GetCSharpSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken).Usings; var newUsings = new List<UsingDirectiveSyntax>(); foreach (var import in importsNode.Elements(XName.Get("Import", snippetNode.Name.NamespaceName))) { var namespaceElement = import.Element(namespaceXmlName); if (namespaceElement == null) { continue; } var namespaceToImport = namespaceElement.Value.Trim(); if (string.IsNullOrEmpty(namespaceToImport)) { continue; } var candidateUsing = SyntaxFactory.ParseCompilationUnit("using " + namespaceToImport + ";").DescendantNodes().OfType<UsingDirectiveSyntax>().FirstOrDefault(); if (candidateUsing == null) { continue; } if (!existingUsings.Any(u => UsingsMatch(u, candidateUsing))) { newUsings.Add(candidateUsing.WithAdditionalAnnotations(Formatter.Annotation).WithAppendedTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed)); } } return newUsings; }