// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.CodeGeneration; internal readonly record struct CodeGenerationSolutionContext( Solution Solution, CodeGenerationContext Context); /// <summary> /// General options for controlling the code produced by the <see cref="CodeGenerator"/> that apply to all documents. /// </summary> internal sealed class CodeGenerationContext { public static readonly CodeGenerationContext Default = new(); /// <summary> /// A location used to determine the best place to generate a member. This is only used for /// determining which part of a partial type to generate in. If a type only has one part, or /// an API is used that specifies the type, then this is not used. A part is preferred if /// it surrounds this context location. If no part surrounds this location then a part is /// preferred if it comes from the same SyntaxTree as this location. If there is no /// such part, then any part may be used for generation. /// /// This option is not necessary if <see cref="AfterThisLocation"/> or <see cref="BeforeThisLocation"/> are /// provided. /// </summary> public Location? ContextLocation { get; } /// <summary> /// A hint to the code generation service to specify where the generated code should be /// placed. Code will be generated after this location if the location is valid in the type /// or symbol being generated into, and it is possible to generate the code after it. /// /// If this option is provided, neither <see cref="ContextLocation"/> nor <see cref="BeforeThisLocation"/> are /// needed. /// </summary> public Location? AfterThisLocation { get; } /// <summary> /// A hint to the code generation service to specify where the generated code should be /// placed. Code will be generated before this location if the location is valid in the type /// or symbol being generated into, and it is possible to generate the code after it. /// /// If this option is provided, neither <see cref="ContextLocation"/> nor <see cref="AfterThisLocation"/> are /// needed. /// </summary> public Location? BeforeThisLocation { get; } /// <summary> /// True if the code generation service should add <see cref="Simplifier.AddImportsAnnotation"/>, /// and when not generating directly into a declaration, should try to automatically add imports to the file /// for any generated code. /// Defaults to true. /// </summary> public bool AddImports { get; } /// <summary> /// Contains additional imports to be automatically added. This is useful for adding /// imports that are part of a list of statements. /// </summary> public IEnumerable<INamespaceSymbol> AdditionalImports { get; } /// <summary> /// True if members of a symbol should also be generated along with the declaration. If /// false, only the symbol's declaration will be generated. /// </summary> public bool GenerateMembers { get; } /// <summary> /// True if the code generator should merge namespaces which only contain other namespaces /// into a single declaration with a dotted name. False if the nesting should be preserved /// and each namespace declaration should be nested and should only have a single non-dotted /// name. /// /// Merging can only occur if the namespace only contains a single member that is also a /// namespace. /// </summary> public bool MergeNestedNamespaces { get; } /// <summary> /// True if the code generation should put multiple attributes in a single attribute /// declaration, or if should have a separate attribute declaration for each attribute. For /// example, in C# setting this to True this would produce "[Goo, Bar]" while setting it to /// False would produce "[Goo][Bar]" /// </summary> public bool MergeAttributes { get; } /// <summary> /// True if the code generator should always generate accessibility modifiers, even if they /// are the same as the defaults for that symbol. For example, a private field in C# does /// not need its accessibility specified as it will be private by default. However, if this /// option is set to true 'private' will still be generated. /// </summary> public bool GenerateDefaultAccessibility { get; } /// <summary> /// True if the code generator should generate empty bodies for methods along with the /// method declaration. If false, only method declarations will be generated. /// </summary> public bool GenerateMethodBodies { get; } /// <summary> /// True if the code generator should generate documentation comments where available /// </summary> public bool GenerateDocumentationComments { get; } /// <summary> /// True if the code generator should automatically attempt to choose the appropriate location /// to insert members. If false and a generation location is not specified by AfterThisLocation, /// or BeforeThisLocation, members will be inserted at the end of the destination definition. /// </summary> public bool AutoInsertionLocation { get; } /// <summary> /// If <see cref="AutoInsertionLocation"/> is <see langword="false"/>, determines if members will be /// sorted before being added to the end of the list of members. /// </summary> public bool SortMembers { get; } /// <summary> /// True if the code generator should attempt to reuse the syntax of the constituent entities, such as members, access modifier tokens, etc. while attempting to generate code. /// If any of the member symbols have zero declaring syntax references (non-source symbols) OR two or more declaring syntax references (partial definitions), then syntax is not reused. /// If false, then the code generator will always synthesize a new syntax node and ignore the declaring syntax references. /// </summary> public bool ReuseSyntax { get; } public CodeGenerationContext( Location? contextLocation = null, Location? afterThisLocation = null, Location? beforeThisLocation = null, bool addImports = true, IEnumerable<INamespaceSymbol>? additionalImports = null, bool generateMembers = true, bool mergeNestedNamespaces = true, bool mergeAttributes = true, bool generateDefaultAccessibility = true, bool generateMethodBodies = true, bool generateDocumentationComments = false, bool autoInsertionLocation = true, bool sortMembers = true, bool reuseSyntax = false) { CheckLocation(contextLocation, nameof(contextLocation)); CheckLocation(afterThisLocation, nameof(afterThisLocation)); CheckLocation(beforeThisLocation, nameof(beforeThisLocation)); ContextLocation = contextLocation; AfterThisLocation = afterThisLocation; BeforeThisLocation = beforeThisLocation; AddImports = addImports; AdditionalImports = additionalImports ?? []; GenerateMembers = generateMembers; MergeNestedNamespaces = mergeNestedNamespaces; MergeAttributes = mergeAttributes; GenerateDefaultAccessibility = generateDefaultAccessibility; GenerateMethodBodies = generateMethodBodies; GenerateDocumentationComments = generateDocumentationComments; AutoInsertionLocation = autoInsertionLocation; SortMembers = sortMembers; ReuseSyntax = reuseSyntax; } private static void CheckLocation(Location? location, string name) { if (location != null && !location.IsInSource) { throw new ArgumentException(WorkspaceExtensionsResources.Location_must_be_null_or_from_source, name); } } internal Location? BestLocation => this.AfterThisLocation ?? this.BeforeThisLocation ?? this.ContextLocation; public CodeGenerationContext With( Optional<Location> contextLocation = default, Optional<Location?> afterThisLocation = default, Optional<Location?> beforeThisLocation = default, Optional<bool> addImports = default, Optional<IEnumerable<INamespaceSymbol>> additionalImports = default, Optional<bool> generateMembers = default, Optional<bool> mergeNestedNamespaces = default, Optional<bool> mergeAttributes = default, Optional<bool> generateDefaultAccessibility = default, Optional<bool> generateMethodBodies = default, Optional<bool> generateDocumentationComments = default, Optional<bool> autoInsertionLocation = default, Optional<bool> sortMembers = default, Optional<bool> reuseSyntax = default) { var newContextLocation = contextLocation.HasValue ? contextLocation.Value : this.ContextLocation; var newAfterThisLocation = afterThisLocation.HasValue ? afterThisLocation.Value : this.AfterThisLocation; var newBeforeThisLocation = beforeThisLocation.HasValue ? beforeThisLocation.Value : this.BeforeThisLocation; var newAddImports = addImports.HasValue ? addImports.Value : this.AddImports; var newAdditionalImports = additionalImports.HasValue ? additionalImports.Value : this.AdditionalImports; var newGenerateMembers = generateMembers.HasValue ? generateMembers.Value : this.GenerateMembers; var newMergeNestedNamespaces = mergeNestedNamespaces.HasValue ? mergeNestedNamespaces.Value : this.MergeNestedNamespaces; var newMergeAttributes = mergeAttributes.HasValue ? mergeAttributes.Value : this.MergeAttributes; var newGenerateDefaultAccessibility = generateDefaultAccessibility.HasValue ? generateDefaultAccessibility.Value : this.GenerateDefaultAccessibility; var newGenerateMethodBodies = generateMethodBodies.HasValue ? generateMethodBodies.Value : this.GenerateMethodBodies; var newGenerateDocumentationComments = generateDocumentationComments.HasValue ? generateDocumentationComments.Value : this.GenerateDocumentationComments; var newAutoInsertionLocation = autoInsertionLocation.HasValue ? autoInsertionLocation.Value : this.AutoInsertionLocation; var newSortMembers = sortMembers.HasValue ? sortMembers.Value : this.SortMembers; var newReuseSyntax = reuseSyntax.HasValue ? reuseSyntax.Value : this.ReuseSyntax; return new CodeGenerationContext( newContextLocation, newAfterThisLocation, newBeforeThisLocation, newAddImports, newAdditionalImports, newGenerateMembers, newMergeNestedNamespaces, newMergeAttributes, newGenerateDefaultAccessibility, newGenerateMethodBodies, newGenerateDocumentationComments, newAutoInsertionLocation, newSortMembers, newReuseSyntax); } } |