// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGeneration
{
///
/// Options for controlling the code produced by the .
///
internal class CodeGenerationOptions
{
public static readonly CodeGenerationOptions Default = new CodeGenerationOptions();
///
/// 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 or are
/// provided.
///
public Location ContextLocation { get; }
///
/// 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 nor are
/// needed.
///
public Location AfterThisLocation { get; }
///
/// 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 nor are
/// needed.
///
public Location BeforeThisLocation { get; }
///
/// True if the code generation service should add ,
/// and when not generating directly into a declaration, should try to automatically add imports to the file
/// for any generated code.
/// Defaults to true.
///
public bool AddImports { get; }
///
/// True if, when adding a System import, the import should be placed above non-System
/// imports. Defaults to true. Only used if is true.
///
public bool PlaceSystemNamespaceFirst { get; }
///
/// Contains additional imports to be automatically added. This is useful for adding
/// imports that are part of a list of statements.
///
public IEnumerable AdditionalImports { get; }
///
/// True if members of a symbol should also be generated along with the declaration. If
/// false, only the symbol's declaration will be generated.
///
public bool GenerateMembers { get; }
///
/// 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.
///
public bool MergeNestedNamespaces { get; }
///
/// 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]"
///
public bool MergeAttributes { get; }
///
/// 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.
///
public bool GenerateDefaultAccessibility { get; }
///
/// True if the code generator should generate empty bodies for methods along with the
/// method declaration. If false, only method declarations will be generated.
///
public bool GenerateMethodBodies { get; }
///
/// True if the code generator should generate documentation comments where available
///
public bool GenerateDocumentationComments { get; }
///
/// 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.
///
public bool AutoInsertionLocation { get; }
///
/// If is , determines if members will be
/// sorted before being added to the end of the list of members.
///
public bool SortMembers { get; }
///
/// 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.
///
public bool ReuseSyntax { get; }
public ParseOptions ParseOptions { get; }
public CodeGenerationOptions(
Location contextLocation = null,
Location afterThisLocation = null,
Location beforeThisLocation = null,
bool addImports = true,
bool placeSystemNamespaceFirst = true,
IEnumerable 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,
ParseOptions parseOptions = null)
{
CheckLocation(contextLocation, nameof(contextLocation));
CheckLocation(afterThisLocation, nameof(afterThisLocation));
CheckLocation(beforeThisLocation, nameof(beforeThisLocation));
this.ContextLocation = contextLocation;
this.AfterThisLocation = afterThisLocation;
this.BeforeThisLocation = beforeThisLocation;
this.AddImports = addImports;
this.PlaceSystemNamespaceFirst = placeSystemNamespaceFirst;
this.AdditionalImports = additionalImports ?? SpecializedCollections.EmptyEnumerable();
this.GenerateMembers = generateMembers;
this.MergeNestedNamespaces = mergeNestedNamespaces;
this.MergeAttributes = mergeAttributes;
this.GenerateDefaultAccessibility = generateDefaultAccessibility;
this.GenerateMethodBodies = generateMethodBodies;
this.GenerateDocumentationComments = generateDocumentationComments;
this.AutoInsertionLocation = autoInsertionLocation;
this.SortMembers = sortMembers;
this.ReuseSyntax = reuseSyntax;
this.ParseOptions = parseOptions ?? this.BestLocation?.SourceTree.Options;
}
private void CheckLocation(Location location, string name)
{
if (location != null && !location.IsInSource)
{
throw new ArgumentException(WorkspacesResources.Location_must_be_null_or_from_source, name);
}
}
internal Location BestLocation
{
get
{
return this.AfterThisLocation != null
? this.AfterThisLocation
: this.BeforeThisLocation != null
? this.BeforeThisLocation
: this.ContextLocation;
}
}
public CodeGenerationOptions With(
Optional contextLocation = default,
Optional afterThisLocation = default,
Optional beforeThisLocation = default,
Optional addImports = default,
Optional placeSystemNamespaceFirst = default,
Optional> additionalImports = default,
Optional generateMembers = default,
Optional mergeNestedNamespaces = default,
Optional mergeAttributes = default,
Optional generateDefaultAccessibility = default,
Optional generateMethodBodies = default,
Optional generateDocumentationComments = default,
Optional autoInsertionLocation = default,
Optional sortMembers = default,
Optional reuseSyntax = default,
Optional parseOptions = 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 newPlaceSystemNamespaceFirst = placeSystemNamespaceFirst.HasValue ? placeSystemNamespaceFirst.Value : this.PlaceSystemNamespaceFirst;
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;
var newParseOptions = parseOptions.HasValue ? parseOptions.Value : this.ParseOptions;
return new CodeGenerationOptions(
newContextLocation,
newAfterThisLocation,
newBeforeThisLocation,
newAddImports,
newPlaceSystemNamespaceFirst,
newAdditionalImports,
newGenerateMembers,
newMergeNestedNamespaces,
newMergeAttributes,
newGenerateDefaultAccessibility,
newGenerateMethodBodies,
newGenerateDocumentationComments,
newAutoInsertionLocation,
newSortMembers,
newReuseSyntax,
newParseOptions);
}
}
}