private static async Task<Solution> GetTransformedSolutionAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var solution = document.Project.Solution; var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var expectedFileName = diagnostic.Properties[SA1649FileNameMustMatchTypeName.ExpectedFileNameKey]; var newPath = document.FilePath != null ? Path.Combine(Path.GetDirectoryName(document.FilePath), expectedFileName) : null; var newDocumentId = DocumentId.CreateNewId(document.Id.ProjectId); var newSolution = solution .RemoveDocument(document.Id) .AddDocument(newDocumentId, expectedFileName, syntaxRoot, document.Folders, newPath); // Make sure to also add the file to linked projects foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) { DocumentId linkedExtractedDocumentId = DocumentId.CreateNewId(linkedDocumentId.ProjectId); newSolution = newSolution.AddDocument(linkedExtractedDocumentId, expectedFileName, syntaxRoot, document.Folders); } return newSolution; }
private static async Task<Solution> GetTransformedSolutionAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); BaseTypeDeclarationSyntax baseTypeDeclarationSyntax = node as BaseTypeDeclarationSyntax; DelegateDeclarationSyntax delegateDeclarationSyntax = node as DelegateDeclarationSyntax; if (baseTypeDeclarationSyntax == null && delegateDeclarationSyntax == null) { return document.Project.Solution; } DocumentId extractedDocumentId = DocumentId.CreateNewId(document.Project.Id); string extractedDocumentName = baseTypeDeclarationSyntax.Identifier.ValueText; if (baseTypeDeclarationSyntax != null) { TypeDeclarationSyntax typeDeclarationSyntax = baseTypeDeclarationSyntax as TypeDeclarationSyntax; if (typeDeclarationSyntax?.TypeParameterList?.Parameters.Count > 0) { extractedDocumentName += "`" + typeDeclarationSyntax.TypeParameterList.Parameters.Count; } } else { if (delegateDeclarationSyntax.TypeParameterList?.Parameters.Count > 0) { extractedDocumentName += "`" + delegateDeclarationSyntax.TypeParameterList.Parameters.Count; } } extractedDocumentName += ".cs"; List<SyntaxNode> nodesToRemoveFromExtracted = new List<SyntaxNode>(); SyntaxNode previous = node; for (SyntaxNode current = node.Parent; current != null; previous = current, current = current.Parent) { foreach (SyntaxNode child in current.ChildNodes()) { if (child == previous) { continue; } switch (child.Kind()) { case SyntaxKind.NamespaceDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.DelegateDeclaration: nodesToRemoveFromExtracted.Add(child); break; default: break; } } } SyntaxNode extractedDocumentNode = root.RemoveNodes(nodesToRemoveFromExtracted, SyntaxRemoveOptions.KeepUnbalancedDirectives); Solution updatedSolution = document.Project.Solution.AddDocument(extractedDocumentId, extractedDocumentName, extractedDocumentNode, document.Folders); // Make sure to also add the file to linked projects foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) { DocumentId linkedExtractedDocumentId = DocumentId.CreateNewId(linkedDocumentId.ProjectId); updatedSolution = updatedSolution.AddDocument(linkedExtractedDocumentId, extractedDocumentName, extractedDocumentNode, document.Folders); } // Remove the type from its original location updatedSolution = updatedSolution.WithDocumentSyntaxRoot(document.Id, root.RemoveNode(node, SyntaxRemoveOptions.KeepUnbalancedDirectives)); return updatedSolution; }
private async Task<Result> EncapsulateFieldAsync(IFieldSymbol field, Document document, bool updateReferences, CancellationToken cancellationToken) { var originalField = field; var finalNames = GeneratePropertyAndFieldNames(field); var finalFieldName = finalNames.Item1; var generatedPropertyName = finalNames.Item2; // Annotate the field declarations so we can find it after rename. var fieldDeclaration = field.DeclaringSyntaxReferences.First(); var declarationAnnotation = new SyntaxAnnotation(); document = document.WithSyntaxRoot(fieldDeclaration.SyntaxTree.GetRoot(cancellationToken).ReplaceNode(fieldDeclaration.GetSyntax(cancellationToken), fieldDeclaration.GetSyntax(cancellationToken).WithAdditionalAnnotations(declarationAnnotation))); var solution = document.Project.Solution; foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) { var linkedDocument = solution.GetDocument(linkedDocumentId); var linkedRoot = await linkedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var linkedFieldNode = linkedRoot.FindNode(fieldDeclaration.Span); if (linkedFieldNode.Span != fieldDeclaration.Span) { continue; } var updatedRoot = linkedRoot.ReplaceNode(linkedFieldNode, linkedFieldNode.WithAdditionalAnnotations(declarationAnnotation)); solution = solution.WithDocumentSyntaxRoot(linkedDocumentId, updatedRoot); } document = solution.GetDocument(document.Id); // Resolve the annotated symbol and prepare for rename. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var compilation = semanticModel.Compilation; field = field.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol as IFieldSymbol; var solutionNeedingProperty = solution; // We couldn't resolve field after annotating its declaration. Bail if (field == null) { return null; } solutionNeedingProperty = await UpdateReferencesAsync( updateReferences, solution, document, field, finalFieldName, generatedPropertyName, cancellationToken).ConfigureAwait(false); document = solutionNeedingProperty.GetDocument(document.Id); var markFieldPrivate = field.DeclaredAccessibility != Accessibility.Private; var rewrittenFieldDeclaration = await RewriteFieldNameAndAccessibility(finalFieldName, markFieldPrivate, document, declarationAnnotation, cancellationToken).ConfigureAwait(false); document = await Formatter.FormatAsync(document.WithSyntaxRoot(rewrittenFieldDeclaration), Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false); solution = document.Project.Solution; foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) { var linkedDocument = solution.GetDocument(linkedDocumentId); var updatedLinkedRoot = await RewriteFieldNameAndAccessibility(finalFieldName, markFieldPrivate, linkedDocument, declarationAnnotation, cancellationToken).ConfigureAwait(false); var updatedLinkedDocument = await Formatter.FormatAsync(linkedDocument.WithSyntaxRoot(updatedLinkedRoot), Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false); solution = updatedLinkedDocument.Project.Solution; } document = solution.GetDocument(document.Id); semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); compilation = semanticModel.Compilation; var newRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newDeclaration = newRoot.GetAnnotatedNodes<SyntaxNode>(declarationAnnotation).First(); field = semanticModel.GetDeclaredSymbol(newDeclaration, cancellationToken) as IFieldSymbol; var generatedProperty = GenerateProperty(generatedPropertyName, finalFieldName, originalField.DeclaredAccessibility, originalField, field.ContainingType, new SyntaxAnnotation(), document, cancellationToken); var solutionWithProperty = await AddPropertyAsync(document, document.Project.Solution, field, generatedProperty, cancellationToken).ConfigureAwait(false); return new Result(solutionWithProperty, originalField.ToDisplayString(), Glyph.FieldPublic); }
public async Task <CompletionData> GetExpressionCompletionDataAsync(string exp, StackFrame frame, CancellationToken token) { var location = frame.SourceLocation; if (document == null) { return(null); } var solution = document.Project.Solution; var textSnapshot = textBuffer.CurrentSnapshot; var text = textSnapshot.GetText(new Span(0, textSnapshot.Length)); var insertOffset = await GetAdjustedContextPointAsync(textSnapshot.GetLineFromLineNumber (location.EndLine - 1).Start.Position + location.EndColumn - 1, document, token).ConfigureAwait(false); text = text.Insert(insertOffset, ";" + exp + ";"); insertOffset++; //advance for 1 which represents `;` before expression var newTextBuffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer(text, textBuffer.ContentType); var snapshot = newTextBuffer.CurrentSnapshot; try { //Workaround Mono bug: https://github.com/mono/mono/issues/8700 snapshot.AsText(); } catch (Exception) { } // Fork the solution using this new primary buffer for the document and all of its linked documents. var forkedSolution = solution.WithDocumentText(document.Id, snapshot.AsText(), PreservationMode.PreserveIdentity); foreach (var link in document.GetLinkedDocumentIds()) { forkedSolution = forkedSolution.WithDocumentText(link, snapshot.AsText(), PreservationMode.PreserveIdentity); } // Put it into a new workspace, and open it and its related documents // with the projection buffer as the text. var forkedWorkspace = new DebuggerIntellisenseWorkspace(forkedSolution); forkedWorkspace.OpenDocument(document.Id, newTextBuffer.AsTextContainer()); foreach (var link in document.GetLinkedDocumentIds()) { forkedWorkspace.OpenDocument(link, newTextBuffer.AsTextContainer()); } var cs = forkedWorkspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService <CompletionService> (); var trigger = new CompletionTrigger(CompletionTriggerKind.Invoke, '\0'); var roslynCompletions = await cs.GetCompletionsAsync(forkedWorkspace.CurrentSolution.GetDocument(document.Id), insertOffset + exp.Length, trigger, cancellationToken : token).ConfigureAwait(false); if (roslynCompletions == null) { return(null); } var result = new Mono.Debugging.Client.CompletionData(); foreach (var roslynCompletion in roslynCompletions.Items) { if (roslynCompletion.Tags.Contains(WellKnownTags.Snippet)) { continue; } result.Items.Add(new Mono.Debugging.Client.CompletionItem(roslynCompletion.DisplayText, RoslynTagsToDebuggerFlags(roslynCompletion.Tags))); } result.ExpressionLength = roslynCompletions.Span.Length; return(result); }
protected override async Task<QuickInfoContent> BuildContentAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) { var linkedDocumentIds = document.GetLinkedDocumentIds(); var modelAndSymbols = await this.BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); if ((modelAndSymbols.Item2 == null || modelAndSymbols.Item2.Count == 0) && !linkedDocumentIds.Any()) { return null; } if (!linkedDocumentIds.Any()) { return await CreateContentAsync(document.Project.Solution.Workspace, token, modelAndSymbols.Item1, modelAndSymbols.Item2, supportedPlatforms: null, cancellationToken: cancellationToken).ConfigureAwait(false); } // Linked files/shared projects: imagine the following when FOO is false // #if FOO // int x = 3; // #endif // var y = x$$; // // 'x' will bind as an error type, so we'll show incorrect information. // Instead, we need to find the head in which we get the best binding, // which in this case is the one with no errors. var candidateProjects = new List<ProjectId>() { document.Project.Id }; var invalidProjects = new List<ProjectId>(); var candidateResults = new List<Tuple<DocumentId, SemanticModel, IList<ISymbol>>>(); candidateResults.Add(Tuple.Create(document.Id, modelAndSymbols.Item1, modelAndSymbols.Item2)); foreach (var link in linkedDocumentIds) { var linkedDocument = document.Project.Solution.GetDocument(link); var linkedToken = await FindTokenInLinkedDocument(token, linkedDocument, cancellationToken).ConfigureAwait(false); if (linkedToken != default(SyntaxToken)) { // Not in an inactive region, so this file is a candidate. candidateProjects.Add(link.ProjectId); var linkedModelAndSymbols = await this.BindTokenAsync(linkedDocument, linkedToken, cancellationToken).ConfigureAwait(false); candidateResults.Add(Tuple.Create(link, linkedModelAndSymbols.Item1, linkedModelAndSymbols.Item2)); } } // Take the first result with no errors. var bestBinding = candidateResults.FirstOrDefault(c => c.Item3.Count > 0 && !ErrorVisitor.ContainsError(c.Item3.FirstOrDefault())); // Every file binds with errors. Take the first candidate, which is from the current file. if (bestBinding == null) { bestBinding = candidateResults.First(); } if (bestBinding.Item3 == null || !bestBinding.Item3.Any()) { return null; } // We calculate the set of supported projects candidateResults.Remove(bestBinding); foreach (var candidate in candidateResults) { // Does the candidate have anything remotely equivalent? if (!candidate.Item3.Intersect(bestBinding.Item3, LinkedFilesSymbolEquivalenceComparer.Instance).Any()) { invalidProjects.Add(candidate.Item1.ProjectId); } } var supportedPlatforms = new SupportedPlatformData(invalidProjects, candidateProjects, document.Project.Solution.Workspace); return await CreateContentAsync(document.Project.Solution.Workspace, token, bestBinding.Item2, bestBinding.Item3, supportedPlatforms, cancellationToken).ConfigureAwait(false); }