/// <summary> /// The execute transaction inner. /// </summary> /// <param name="solution"> /// The solution. /// </param> /// <param name="textControl"> /// The text control. /// </param> public override void ExecuteTransactionInner(ISolution solution, ITextControl textControl) { IDeclaration declaration = Utils.GetTypeClosestToTextControl<IDeclaration>(solution, textControl); DeclarationHeader declarationHeader = new DeclarationHeader(declaration); this.ProcessXmlNode(declarationHeader.XmlNode, this.DeprecatedWord, this.AlternateWord); declarationHeader.Update(); }
/// <summary> /// Removes a return element if it currently has one. /// </summary> /// <param name="memberDeclaration"> /// The <see cref="ITypeDeclaration"/> to check and fix. /// </param> public void RemoveReturnsElement(ITypeMemberDeclaration memberDeclaration) { DeclarationHeader declarationHeader = new DeclarationHeader(memberDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasReturns) { return; } declarationHeader.XmlNode.RemoveChild(declarationHeader.ReturnsXmlNode); declarationHeader.Update(); }
/// <summary> /// Inserts a returns element to the element if its missing. /// </summary> /// <param name="memberDeclaration"> /// The <see cref="ITypeMemberDeclaration"/> to check and fix. /// </param> /// <param name="returnType"> /// The text to insert as the return type. /// </param> public void InsertReturnsElement(ITypeMemberDeclaration memberDeclaration, string returnType) { Param.RequireNotNull(memberDeclaration, "memberDeclaration"); DeclarationHeader declarationHeader = new DeclarationHeader(memberDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } string valueText = string.Empty; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, memberDeclaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { valueText = string.Format("The <see cref=\"{0}\"/>.", returnType.SubstringBefore('{')); } XmlNode xmlNode = declarationHeader.XmlNode; XmlNode returnsXmlNode = declarationHeader.ReturnsXmlNode; if (declarationHeader.HasReturns) { if (string.IsNullOrEmpty(returnsXmlNode.InnerText.Trim())) { returnsXmlNode.InnerXml = valueText; declarationHeader.Update(); } } else { XmlNode valueNode = CreateNode(xmlNode, "returns"); valueNode.InnerXml = valueText; xmlNode.AppendChild(valueNode); declarationHeader.Update(); } }
/// <summary> /// Inserts a value element to the element if its missing. /// </summary> /// <param name="propertyDeclaration"> /// The <see cref="IPropertyDeclaration"/> to check and fix. /// </param> public void InsertValueElement(IPropertyDeclaration propertyDeclaration) { DeclarationHeader declarationHeader = new DeclarationHeader(propertyDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; string valueText = string.Empty; XmlNode valueXmlNode = declarationHeader.ValueXmlNode; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, propertyDeclaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { valueText = string.Format("The {0}.", Utils.ConvertTextToSentence(propertyDeclaration.DeclaredName).ToLower()); } if (declarationHeader.HasValue) { if (string.IsNullOrEmpty(valueXmlNode.InnerText.Trim())) { valueXmlNode.InnerText = valueText; declarationHeader.Update(); } } else { XmlNode valueNode = CreateNode(xmlNode, "value"); valueNode.InnerText = valueText; xmlNode.AppendChild(valueNode); declarationHeader.Update(); } }
/// <summary> /// Checks declaration comment blocks. /// </summary> /// <param name="file"> /// The <see cref="ICSharpFile"/> to use. /// </param> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to check. /// </param> /// <param name="options"> /// <see cref="OrderingOptions"/>Current options that we can reference. /// </param> public void CheckDeclarationDocumentation(ICSharpFile file, IDeclaration declaration, DocumentationOptions options) { Param.RequireNotNull(file, "file"); Param.RequireNotNull(declaration, "declaration"); Param.Ignore(options); bool insertMissingElementDocOption = true; bool documentationTextMustBeginWithACapitalLetter = true; bool documentationTextMustEndWithAPeriod = true; bool elementDocumentationMustHaveSummary = true; bool constructorSummaryDocBeginsWithStandardText = true; bool destructorSummaryDocBeginsWithStandardText = true; bool propertyDocumentationMustHaveValueDocumented = true; bool insertMissingParamTagOption = true; bool genericTypeParametersMustBeDocumented = true; if (options != null) { insertMissingElementDocOption = options.SA1600ElementsMustBeDocumented; documentationTextMustBeginWithACapitalLetter = options.SA1628DocumentationTextMustBeginWithACapitalLetter; documentationTextMustEndWithAPeriod = options.SA1629DocumentationTextMustEndWithAPeriod; elementDocumentationMustHaveSummary = options.SA1604ElementDocumentationMustHaveSummary; constructorSummaryDocBeginsWithStandardText = options.SA1642ConstructorSummaryDocumentationMustBeginWithStandardText; destructorSummaryDocBeginsWithStandardText = options.SA1643DestructorSummaryDocumentationMustBeginWithStandardText; propertyDocumentationMustHaveValueDocumented = options.SA1609PropertyDocumentationMustHaveValue; insertMissingParamTagOption = options.SA1611ElementParametersMustBeDocumented; genericTypeParametersMustBeDocumented = options.SA1618GenericTypeParametersMustBeDocumented; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); bool formatSummary = false; if (insertMissingElementDocOption && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1600) && declarationHeader.IsMissing) { formatSummary = this.InsertMissingDeclarationHeader(file, declaration); } if (elementDocumentationMustHaveSummary && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1604) && !declarationHeader.HasSummary) { formatSummary = formatSummary | this.InsertMissingSummaryElement(declaration); } if (formatSummary) { this.FormatSummaryElement(declaration); } if (declaration is IConstructorDeclaration) { if (insertMissingParamTagOption && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1611)) { IConstructorDeclaration constructorDeclaration = declaration as IConstructorDeclaration; if (constructorDeclaration.ParameterDeclarations.Count > 0) { this.InsertMissingParamElement(constructorDeclaration); } } if (constructorSummaryDocBeginsWithStandardText && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1642)) { this.EnsureConstructorSummaryDocBeginsWithStandardText(declaration as IConstructorDeclaration); } } DocumentationRulesConfiguration docConfig = this.GetDocumentationRulesConfig(file); // However it can be on/off depending on the file so we'd have to cache it per file bool ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("DocumentationTextMustBeginWithACapitalLetter"); if (documentationTextMustBeginWithACapitalLetter && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1628)) { this.EnsureDocumentationTextIsUppercase(declaration); } ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("DocumentationTextMustEndWithAPeriod"); if (documentationTextMustEndWithAPeriod && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1629)) { this.EnsureDocumentationTextEndsWithAPeriod(declaration); } if (declaration is IDestructorDeclaration) { if (destructorSummaryDocBeginsWithStandardText && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1643)) { this.EnsureDestructorSummaryDocBeginsWithStandardText(declaration as IDestructorDeclaration); } } if (declaration is IMethodDeclaration || declaration is IIndexerDeclaration) { this.CheckMethodAndIndexerDeclarationDocumentation(declaration as IParametersOwnerDeclaration, options); } if (declaration is IPropertyDeclaration) { ruleIsEnabled = docConfig.GetStyleCopRuleEnabled("PropertyDocumentationMustHaveValue"); if (propertyDocumentationMustHaveValueDocumented && ruleIsEnabled && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1609)) { this.InsertValueElement(declaration as IPropertyDeclaration); } } if (declaration is ITypeParametersOwner && (genericTypeParametersMustBeDocumented && !Utils.IsRuleSuppressed(declaration, StyleCopRules.SA1618))) { this.InsertMissingTypeParamElement(declaration); } }
/// <summary> /// Updates the summary to include all <c>typeparam</c> and remove any extra ones and in the correct order. /// </summary> /// <param name="declaration"> /// The <see cref="ITypeDeclaration"/> to check and fix. /// </param> public void InsertMissingTypeParamElement(IDeclaration declaration) { ITypeParametersOwner declaredElement = declaration.DeclaredElement as ITypeParametersOwner; if (declaredElement == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; Hashtable ht = new Hashtable(); foreach (ITypeParameter parameter in declaredElement.TypeParameters) { ht.Add(parameter.ShortName, null); if (declarationHeader.ContainsTypeParameter(parameter.ShortName)) { continue; } XmlNode parameterNode = CreateTypeParamNode(xmlNode, parameter.ShortName); XmlNodeList paramNodeList = xmlNode.SelectNodes("//typeparam"); if (paramNodeList != null) { XmlNode c = paramNodeList.Count == 0 ? declarationHeader.SummaryXmlNode : paramNodeList.Item(paramNodeList.Count - 1); xmlNode.InsertAfter(parameterNode, c); } } RemoveTypeParamsNotRequired(xmlNode, ht); ReorderTypeParams(xmlNode, declaredElement.TypeParameters); declarationHeader.Update(); }
/// <summary> /// Insert a missing parameter element to the comment. /// </summary> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to check and fix. /// </param> public void InsertMissingParamElement(IDeclaration declaration) { Param.RequireNotNull(declaration, "declaration"); IParametersOwnerDeclaration parametersOwnerDeclaration = declaration as IParametersOwnerDeclaration; if (parametersOwnerDeclaration == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } XmlNode xmlNode = declarationHeader.XmlNode; Hashtable ht = new Hashtable(); IList<IParameterDeclaration> parameters = parametersOwnerDeclaration.ParameterDeclarations; if (parameters != null) { foreach (IParameterDeclaration parameter in parameters) { ht.Add(parameter.DeclaredName, null); if (declarationHeader.ContainsParameter(parameter.DeclaredName)) { continue; } XmlNodeList paramNodeList = xmlNode.SelectNodes("//param"); if (paramNodeList != null) { XmlNode c = paramNodeList.Count == 0 ? declarationHeader.SummaryXmlNode : paramNodeList.Item(paramNodeList.Count - 1); XmlNode parameterNode = CreateParamNode(xmlNode, parameter); xmlNode.InsertAfter(parameterNode, c); } } } RemoveParamsNotRequired(xmlNode, ht); ReorderParams(xmlNode, parameters); declarationHeader.Update(); }
/// <summary> /// Inserts a missing summary element. /// </summary> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to get comment from. /// </param> /// <returns> /// True if the element was inserted. /// </returns> public bool InsertMissingSummaryElement(IDeclaration declaration) { bool returnValue = false; DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return false; } string summaryText = string.Empty; IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, declaration.GetSolution()); if (settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { string text; if (declaration is IInterfaceDeclaration) { text = declaration.DeclaredName.Substring(1) + " interface"; } else { text = Utils.ConvertTextToSentence(declaration.DeclaredName).ToLower(); } summaryText = string.Format("The {0}.", text); } summaryText = Utils.UpdateTextWithToDoPrefixIfRequired(summaryText, settingsStore); XmlNode summaryXmlNode = declarationHeader.SummaryXmlNode; if (declarationHeader.HasSummary) { if (declarationHeader.HasEmptySummary) { summaryXmlNode.InnerText = summaryText; declarationHeader.Update(); returnValue = true; } } else { XmlNode newChild = CreateNode(declarationHeader.XmlNode, "summary"); newChild.InnerText = summaryText; declarationHeader.XmlNode.InsertBefore(newChild, declarationHeader.XmlNode.FirstChild); declarationHeader.Update(); returnValue = true; } return returnValue; }
/// <summary> /// Formats a summary element. /// </summary> /// <param name="declaration"> /// The <see cref="IDeclaration"/> to format the text for. /// </param> public void FormatSummaryElement(IDeclaration declaration) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || declarationHeader.HasEmptySummary || !declarationHeader.HasSummary) { return; } declarationHeader.Update(); }
/// <summary> /// Ensures the declaration passed has its comments beginning with a capital letter. /// </summary> /// <param name="declaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDocumentationTextIsUppercase(IDeclaration declaration) { IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, declaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } this.SwapToUpper(declarationHeader.XmlNode); declarationHeader.Update(); }
/// <summary> /// Ensures the declaration passed has no blank lines unless inside code elements. /// </summary> /// <param name="declaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDocumentationHasNoBlankLines(IDeclaration declaration) { DeclarationHeader declarationHeader = new DeclarationHeader(declaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited) { return; } this.RemoveBlankLines(declarationHeader.XmlNode); declarationHeader.Update(); }
/// <summary> /// Ensures that the destructor documentation starts with the standard text summary. /// </summary> /// <remarks> /// Keeps the existing comment, but prepends the standard text. /// </remarks> /// <param name="destructorDeclaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureDestructorSummaryDocBeginsWithStandardText(IDestructorDeclaration destructorDeclaration) { if (destructorDeclaration == null) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(destructorDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasSummary) { return; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, destructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } string destructorDescriptionText = Utils.CreateDestructorDescriptionText(destructorDeclaration, true); string xmlComment = Utils.GetTextFromDeclarationHeader(declarationHeader.XmlNode); string textWeShouldStartWith = string.Format(CultureInfo.InvariantCulture, CachedCodeStrings.HeaderSummaryForDestructor, destructorDescriptionText); if (!xmlComment.StartsWith(textWeShouldStartWith, StringComparison.Ordinal)) { string summaryText = Utils.CreateSummaryForDestructorDeclaration(destructorDeclaration); declarationHeader.SummaryXmlNode.InnerXml = Environment.NewLine + summaryText; declarationHeader.Update(); } }
/// <summary> /// Ensures that the constructor documentation starts with the standard text summary. /// </summary> /// <remarks> /// Keeps the existing comment, but prepends the standard text. /// </remarks> /// <param name="constructorDeclaration"> /// The destructor <see cref="IDeclaration"/>. /// </param> public void EnsureConstructorSummaryDocBeginsWithStandardText(IConstructorDeclaration constructorDeclaration) { if (constructorDeclaration == null) { return; } IContextBoundSettingsStore settingsStore = PsiSourceFileExtensions.GetSettingsStore(null, constructorDeclaration.GetSolution()); if (!settingsStore.GetValue((StyleCopOptionsSettingsKey key) => key.InsertTextIntoDocumentation)) { return; } DeclarationHeader declarationHeader = new DeclarationHeader(constructorDeclaration); if (declarationHeader.IsMissing || declarationHeader.IsInherited || !declarationHeader.HasSummary) { return; } string existingSummaryText = declarationHeader.SummaryXmlNode.InnerXml; bool parentIsStruct = Utils.IsContainingTypeAStruct(constructorDeclaration); int constructorParameterCount = constructorDeclaration.ParameterDeclarations.Count; string xmlComment = Utils.GetTextFromDeclarationHeader(declarationHeader.XmlNode); string structOrClass = parentIsStruct ? CachedCodeStrings.StructText : CachedCodeStrings.ClassText; string textWeShouldStartWith; if (constructorDeclaration.IsStatic) { textWeShouldStartWith = string.Format( CultureInfo.InvariantCulture, CachedCodeStrings.HeaderSummaryForStaticConstructor, constructorDeclaration.DeclaredName, structOrClass); } else if (constructorDeclaration.GetAccessRights() == AccessRights.PRIVATE && constructorParameterCount == 0) { textWeShouldStartWith = string.Format( CultureInfo.InvariantCulture, CachedCodeStrings.HeaderSummaryForPrivateInstanceConstructor, constructorDeclaration.DeclaredName, structOrClass); } else { string constructorDescriptionText = Utils.CreateConstructorDescriptionText(constructorDeclaration, true); textWeShouldStartWith = string.Format( CultureInfo.InvariantCulture, CachedCodeStrings.HeaderSummaryForInstanceConstructor, constructorDescriptionText, structOrClass); } if (constructorDeclaration.IsStatic) { string docStd = string.Format("Initializes the {0} class.", constructorDeclaration.DeclaredName); if (xmlComment == docStd) { existingSummaryText = string.Empty; } } if (!xmlComment.StartsWith(textWeShouldStartWith, StringComparison.Ordinal)) { string newSummaryText = Utils.CreateSummaryForConstructorDeclaration(constructorDeclaration); declarationHeader.SummaryXmlNode.InnerXml = newSummaryText + " " + existingSummaryText; declarationHeader.Update(); } }