private static async Task FormatProjectAsync(Project project, CodeFormatterOptions options, CancellationToken cancellationToken) { Solution solution = project.Solution; string solutionDirectory = Path.GetDirectoryName(solution.FilePath); WriteLine($" Analyze '{project.Name}'", Verbosity.Minimal); SyntaxFactsService syntaxFacts = SyntaxFactsServiceFactory.Instance.GetService(project.Language); Project newProject = await CodeFormatter.FormatProjectAsync(project, syntaxFacts, options, cancellationToken); ImmutableArray <DocumentId> formattedDocuments = await CodeFormatter.GetFormattedDocumentsAsync(project, newProject, syntaxFacts); WriteFormattedDocuments(formattedDocuments, project, solutionDirectory); if (formattedDocuments.Length > 0) { Solution newSolution = newProject.Solution; WriteLine($"Apply changes to solution '{newSolution.FilePath}'", Verbosity.Normal); if (!solution.Workspace.TryApplyChanges(newSolution)) { Debug.Fail($"Cannot apply changes to solution '{newSolution.FilePath}'"); WriteLine($"Cannot apply changes to solution '{newSolution.FilePath}'", ConsoleColor.Yellow, Verbosity.Diagnostic); } } WriteLine(); WriteLine($"{formattedDocuments.Length} {((formattedDocuments.Length == 1) ? "document" : "documents")} formatted", ConsoleColor.Green, Verbosity.Normal); WriteLine(); }
internal static async Task <ImmutableArray <DocumentId> > GetFormattedDocumentsAsync( Project project, Project newProject, SyntaxFactsService syntaxFacts) { ImmutableArray <DocumentId> .Builder builder = null; foreach (DocumentId documentId in newProject .GetChanges(project) .GetChangedDocuments(onlyGetDocumentsWithTextChanges: true)) { Document document = newProject.GetDocument(documentId); // https://github.com/dotnet/roslyn/issues/30674 if ((await document.GetTextChangesAsync(project.GetDocument(documentId)).ConfigureAwait(false)).Any()) { #if DEBUG bool success = await Utilities.VerifySyntaxEquivalenceAsync(project.GetDocument(document.Id), document, syntaxFacts).ConfigureAwait(false); #endif (builder ?? (builder = ImmutableArray.CreateBuilder <DocumentId>())).Add(document.Id); } } return(builder?.ToImmutableArray() ?? ImmutableArray <DocumentId> .Empty); }
public static async Task <Project> FormatProjectAsync( Project project, SyntaxFactsService syntaxFacts, CodeFormatterOptions options, CancellationToken cancellationToken = default) { if (options == null) { options = CodeFormatterOptions.Default; } foreach (DocumentId documentId in project.DocumentIds) { Document document = project.GetDocument(documentId); if (options.IncludeGeneratedCode || !GeneratedCodeUtility.IsGeneratedCodeFile(document.FilePath)) { SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (options.IncludeGeneratedCode || !syntaxFacts.BeginsWithAutoGeneratedComment(root)) { DocumentOptionSet optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); Document newDocument = await Formatter.FormatAsync(document, optionSet, cancellationToken).ConfigureAwait(false); project = newDocument.Project; } } } return(project); }
public static Task <Project> FormatProjectAsync( Project project, SyntaxFactsService syntaxFacts, CancellationToken cancellationToken = default) { return(FormatProjectAsync(project, syntaxFacts, default(CodeFormatterOptions), cancellationToken)); }
private async Task FormatSolutionAsync(Solution solution, CodeFormatterOptions options, CancellationToken cancellationToken) { string solutionDirectory = Path.GetDirectoryName(solution.FilePath); WriteLine($"Analyze solution '{solution.FilePath}'", ConsoleColor.Cyan, Verbosity.Minimal); Stopwatch stopwatch = Stopwatch.StartNew(); var changedDocuments = new ConcurrentBag <ImmutableArray <DocumentId> >(); Parallel.ForEach(FilterProjects(solution, Options), project => { WriteLine($" Analyze '{project.Name}'", Verbosity.Minimal); SyntaxFactsService syntaxFacts = SyntaxFactsServiceFactory.Instance.GetService(project.Language); Project newProject = CodeFormatter.FormatProjectAsync(project, syntaxFacts, options, cancellationToken).Result; ImmutableArray <DocumentId> formattedDocuments = CodeFormatter.GetFormattedDocumentsAsync(project, newProject, syntaxFacts).Result; if (formattedDocuments.Any()) { changedDocuments.Add(formattedDocuments); WriteFormattedDocuments(formattedDocuments, project, solutionDirectory); } WriteLine($" Done analyzing '{project.Name}'", Verbosity.Normal); }); if (changedDocuments.Count > 0) { Solution newSolution = solution; foreach (DocumentId documentId in changedDocuments.SelectMany(f => f)) { SourceText sourceText = await solution.GetDocument(documentId).GetTextAsync(cancellationToken); newSolution = newSolution.WithDocumentText(documentId, sourceText); } WriteLine($"Apply changes to solution '{solution.FilePath}'", Verbosity.Normal); if (!solution.Workspace.TryApplyChanges(newSolution)) { Debug.Fail($"Cannot apply changes to solution '{solution.FilePath}'"); WriteLine($"Cannot apply changes to solution '{solution.FilePath}'", ConsoleColor.Yellow, Verbosity.Diagnostic); } } WriteLine(); WriteLine($"{changedDocuments.Count} {((changedDocuments.Count == 1) ? "document" : "documents")} formatted", ConsoleColor.Green, Verbosity.Normal); WriteLine(); WriteLine($"Done formatting solution '{solution.FilePath}' in {stopwatch.Elapsed:mm\\:ss\\.ff}", Verbosity.Minimal); }
internal EnvDTE.CodeElement CodeElementFromPosition(int position, EnvDTE.vsCMElement scope) { var root = GetSyntaxRoot(); var leftToken = SyntaxFactsService.FindTokenOnLeftOfPosition(root, position); var rightToken = SyntaxFactsService.FindTokenOnRightOfPosition(root, position); // We apply a set of heuristics to determine which member we pick to start searching. var token = leftToken; if (leftToken != rightToken) { if (leftToken.Span.End == position && rightToken.SpanStart == position) { // If both tokens are touching, we prefer identifiers and keywords to // separators. Note that the language doesn't allow both tokens to be a // keyword or identifier. if (SyntaxFactsService.IsKeyword(rightToken) || SyntaxFactsService.IsIdentifier(rightToken)) { token = rightToken; } } else if (leftToken.Span.End < position && rightToken.SpanStart <= position) { // If only the right token is touching, we have to use it. token = rightToken; } } // If we ended up using the left token but the position is after that token, // walk up to the first node who's last token is not the leftToken. By doing this, we // ensure that we don't find members when the position is actually between them. // In that case, we should find the enclosing type or namespace. var parent = token.Parent; if (token == leftToken && position > token.Span.End) { while (parent != null) { if (parent.GetLastToken() == token) { parent = parent.Parent; } else { break; } } } var node = parent != null ? parent.AncestorsAndSelf().FirstOrDefault(n => CodeModelService.MatchesScope(n, scope)) : null; if (node == null) { return(null); } if (scope == EnvDTE.vsCMElement.vsCMElementAttribute || scope == EnvDTE.vsCMElement.vsCMElementImportStmt || scope == EnvDTE.vsCMElement.vsCMElementParameter || scope == EnvDTE.vsCMElement.vsCMElementOptionStmt || scope == EnvDTE.vsCMElement.vsCMElementInheritsStmt || scope == EnvDTE.vsCMElement.vsCMElementImplementsStmt || (scope == EnvDTE.vsCMElement.vsCMElementFunction && CodeModelService.IsAccessorNode(node))) { // Attributes, imports, parameters, Option, Inherts and Implements // don't have node keys of their own and won't be included in our // collection of elements. Delegate to the service to create these. return(CodeModelService.CreateInternalCodeElement(State, this, node)); } return(CreateCodeElement <EnvDTE.CodeElement>(node)); }
public bool IsValidID(string name) { return(SyntaxFactsService.IsValidIdentifier(name)); }
public bool IsValidID(string name) => SyntaxFactsService.IsValidIdentifier(name);