/// <summary> /// Creates the XML open tag string for an XElement. /// </summary> /// <param name="element">The element.</param> /// <returns>The XML open tag. In case of an element without value, the tag is self-closing.</returns> private static string CreateXmlOpenTag(XElement element, IXmlTagOptions options) { var builder = new StringBuilder(); var name = element.Name.LocalName; builder.Append("<"); builder.Append(TagCase(name, options.Case)); if (element.HasAttributes) { foreach (var attr in element.Attributes()) { builder.Append(CodeCommentHelper.Spacer); builder.Append(attr); } } if (element.IsEmpty) { if (options.SpaceSelfClosing) { builder.Append(CodeCommentHelper.Spacer); } builder.Append("/"); } builder.Append(">"); var result = builder.ToString(); return(options.KeepTogether ? CodeCommentHelper.SpaceToFake(result) : result); }
/// <summary> /// Reformat all comments between the specified start and end point. Comments that start /// within the range, even if they overlap the end are included. /// </summary> /// <param name="textDocument">The text document.</param> /// <param name="start">The start point.</param> /// <param name="end">The end point.</param> public bool FormatComments(TextDocument textDocument, EditPoint start, EditPoint end) { bool foundComments = false; int tabSize = CodeCommentHelper.GetTabSize(_package, textDocument); while (start.Line <= end.Line) { if (CodeCommentHelper.IsCommentLine(start)) { var comment = new CodeComment(start, tabSize); if (comment.IsValid) { comment.Format(); foundComments = true; } if (comment.EndPoint != null) { start = comment.EndPoint.CreateEditPoint(); } } if (start.Line == textDocument.EndPoint.Line) { break; } start.LineDown(); start.StartOfLine(); } return(foundComments); }
/// <summary> /// Reformat all comments between the specified start and end point. Comments that start /// within the range, even if they overlap the end are included. /// </summary> /// <param name="textDocument">The text document.</param> /// <param name="start">The start point.</param> /// <param name="end">The end point.</param> public bool FormatComments(TextDocument textDocument, EditPoint start, EditPoint end) { var options = new CodeCommentOptions(Settings.Default, _package, textDocument); bool foundComments = false; while (start.Line <= end.Line) { if (CodeCommentHelper.IsCommentLine(start)) { var comment = new CodeComment(start); if (comment.IsValid) { comment.Format(options); foundComments = true; } if (comment.EndPoint != null) { start = comment.EndPoint.CreateEditPoint(); } } if (start.Line == textDocument.EndPoint.Line) { break; } start.LineDown(); start.StartOfLine(); } return(foundComments); }
public static string Format(string text) { var xml = XElement.Parse(string.Format("<doc>{0}</doc>", text)); var line = new CommentLineXml(xml); var regex = CodeCommentHelper.GetCommentRegex("CSharp", false); var formatter = new CommentFormatter(line, string.Empty, 4, regex); return(formatter.ToString()); }
/// <summary> /// Helper function to generate the preview in the options menu. /// </summary> public static string FormatXml(string text) { var xml = XElement.Parse($"<doc>{text}</doc>"); var line = new CommentLineXml(xml); var regex = CodeCommentHelper.GetCommentRegex(CodeLanguage.CSharp, false); var formatter = new CommentFormatter(line, "///", 4, regex); return(formatter.ToString()); }
/// <summary> /// Helper function to generate the preview in the options menu. /// </summary> public static string FormatXml(string text, CodeCommentOptions options) { var xml = XElement.Parse(string.Format("<doc>{0}</doc>", text)); var line = new CommentLineXml(xml, options); var regex = CodeCommentHelper.GetCommentRegex("CSharp", false); var formatter = new CommentFormatter(line, "///", options, regex); return(formatter.ToString()); }
public static string Format(string text, string prefix) { var xml = XElement.Parse($"<doc>{text}</doc>"); var line = new CommentLineXml(xml); var regex = CodeCommentHelper.GetCommentRegex(CodeLanguage.CSharp, !string.IsNullOrEmpty(prefix)); var formatter = new CommentFormatter(line, prefix, 4, regex); return(formatter.ToString()); }
private void Append(string value) { if (String.IsNullOrEmpty(value)) { return; } _builder.Append(Settings.Default.Formatting_CommentXmlKeepTagsTogether ? CodeCommentHelper.FakeToSpace(value) : value); _currentPosition += WordLength(value); _isFirstWord = false; }
/// <summary> /// Formats the comment. /// </summary> public TextPoint Format() { if (!IsValid) { throw new InvalidOperationException("Cannot format comment, the comment is not valid."); } var originalText = _startPoint.GetText(_endPoint); var matches = _commentLineRegex.Matches(originalText).OfType <Match>().ToArray(); var commentOptions = new CommentOptions { Prefix = matches.First(m => m.Success).Groups["prefix"].Value ?? string.Empty, Regex = CodeCommentHelper.GetCommentRegex(_document.GetCodeLanguage(), false) }; // Concatenate the comment lines without comment prefixes and see if the resulting bit // can be parsed as XML. ICommentLine line = null; var lineTexts = matches.Select(m => m.Groups["line"].Value).ToArray(); var commentText = string.Join(Environment.NewLine, lineTexts); if (commentText.Contains('<')) { try { var xml = XElement.Parse($"<doc>{commentText}</doc>"); line = new CommentLineXml(xml); } catch (System.Xml.XmlException) { // If XML cannot be parsed, comment will be handled as a normal text comment. } } if (line == null) { line = new CommentLine(commentText); } var formatter = new CommentFormatter( line, _formatterOptions, commentOptions); if (!formatter.Equals(originalText)) { var cursor = StartPoint.CreateEditPoint(); cursor.Delete(EndPoint); cursor.Insert(formatter.ToString()); _endPoint = cursor.CreateEditPoint(); } return(EndPoint); }
/// <summary> /// Initializes a new instance of the <see cref="CodeComment" /> class. /// </summary> public CodeComment(TextPoint point) { if (point == null) { throw new ArgumentNullException("point"); } _document = point.Parent; _commentLineRegex = CodeCommentHelper.GetCommentRegex(_document.Language, true); Expand(point); }
/// <summary> /// Called to update the current status of the command. /// </summary> protected override void OnBeforeQueryStatus() { var activeTextDocument = ActiveTextDocument; var enable = false; if (activeTextDocument != null) { // Enable formatting if there is a comment pattern defined for this document. enable = CodeCommentHelper.GetCommentPrefix(activeTextDocument) != null; } Enabled = enable; }
/// <summary> /// Initializes a new instance of the <see cref="CodeComment" /> class. /// </summary> public CodeComment(TextPoint point, FormatterOptions options) { if (point == null) { throw new ArgumentNullException(nameof(point)); } _document = point.Parent; _commentLineRegex = CodeCommentHelper.GetCommentRegex(_document.GetCodeLanguage()); _formatterOptions = options; Expand(point); }
/// <summary> /// Initializes a new instance of the <see cref="CodeComment" /> class. /// </summary> public CodeComment(TextPoint point, int tabSize) { if (point == null) { throw new ArgumentNullException(nameof(point)); } _document = point.Parent; _commentLineRegex = CodeCommentHelper.GetCommentRegex(_document.GetCodeLanguage()); _tabSize = tabSize; Expand(point); }
/// <summary> /// Called to execute the command. /// </summary> protected override void OnExecute() { base.OnExecute(); var activeTextDocument = ActiveTextDocument; if (activeTextDocument != null && activeTextDocument.Selection != null) { var prefix = CodeCommentHelper.GetCommentPrefix(activeTextDocument); if (prefix != null) { var selection = activeTextDocument.Selection; EditPoint start; EditPoint end; if (selection.IsEmpty) { start = selection.ActivePoint.CreateEditPoint(); end = start.CreateEditPoint(); end.EndOfLine(); } else { start = selection.TopPoint.CreateEditPoint(); start.StartOfLine(); end = selection.BottomPoint.CreateEditPoint(); end.EndOfLine(); } bool foundComments = false; _undoTransactionHelper.Run(() => foundComments = _commentFormatLogic.FormatComments(activeTextDocument, start, end)); if (foundComments) { Package.IDE.StatusBar.Text = Resources.CodeMaidFinishedFormattingTheComment; } else { Package.IDE.StatusBar.Text = string.Format( foundComments ? Resources.CodeMaidFinishedFormattingTheComments0 : Resources.CodeMaidDidNotFindANonCodeComment0ToReformat, selection.IsEmpty ? Resources.UnderTheCursor : Resources.InTheSelection ); } } } }
/// <summary> /// Called to execute the command. /// </summary> protected override void OnExecute() { base.OnExecute(); var activeTextDocument = ActiveTextDocument; if (activeTextDocument != null && activeTextDocument.Selection != null) { var prefix = CodeCommentHelper.GetCommentPrefix(activeTextDocument); if (prefix != null) { var selection = activeTextDocument.Selection; EditPoint start; EditPoint end; if (selection.IsEmpty) { start = selection.ActivePoint.CreateEditPoint(); end = start.CreateEditPoint(); end.EndOfLine(); } else { start = selection.TopPoint.CreateEditPoint(); start.StartOfLine(); end = selection.BottomPoint.CreateEditPoint(); end.EndOfLine(); } bool foundComments = false; _undoTransactionHelper.Run(() => foundComments = _commentFormatLogic.FormatComments(activeTextDocument, start, end)); if (foundComments) { Package.IDE.StatusBar.Text = "CodeMaid finished formatting the comment."; } else { Package.IDE.StatusBar.Text = string.Format( foundComments ? "CodeMaid finished formatting the comments {0}." : "CodeMaid did not find a non-code comment {0} to reformat.", selection.IsEmpty ? "under the cursor" : "in the selection" ); } } } }
/// <summary> /// Called to update the current status of the command. /// </summary> protected override void OnBeforeQueryStatus() { var activeTextDocument = ActiveTextDocument; var enable = false; // Disable comment formatting if using POSIX Regular Expressions (i.e. pre-Visual Studio // 11 versions) since not supported. if (activeTextDocument != null && !Package.UsePOSIXRegEx) { // Enable formatting if there is a comment pattern defined for this document. enable = CodeCommentHelper.GetCommentPrefix(activeTextDocument) != null; } Enabled = enable; }
private void AddCodeComment(CodeTypeMember codeTypeMember, IEnumerable <ICodeCommentEntity> codeComments, bool isDocComment) { if (isDocComment) { CodeCommentStatementCollection descriptionCodeComment = CodeCommentHelper.BuildCodeCommentStatementCollection(codeComments); codeTypeMember.Comments.AddRange(descriptionCodeComment); } else { foreach (var codeComment in codeComments) { CodeCommentStatement descriptionCodeComment = CodeCommentHelper.BuildCodeCommentStatement(codeComment, isDocComment); codeTypeMember.Comments.Add(descriptionCodeComment); } } }
public CommentLineXml(XElement xml, CodeCommentOptions options) : base(null) { TagName = xml.Name.LocalName; // Tags that are forced to be their own line should never be self closing. This prevents // empty tags from getting collapsed. OpenTag = CodeCommentHelper.CreateXmlOpenTag(xml, options, false); Closetag = CodeCommentHelper.CreateXmlCloseTag(xml, options, false); Lines = new List <ICommentLine>(); _innerText = new StringBuilder(); ParseChildNodes(xml, options); CloseInnerText(); }
/// <summary> /// Reformat all comments between the specified start and end point. Comments that start /// within the range, even if they overlap the end are included. /// </summary> /// <param name="textDocument">The text document.</param> /// <param name="start">The start point.</param> /// <param name="end">The end point.</param> public bool FormatComments(TextDocument textDocument, EditPoint start, EditPoint end) { bool foundComments = false; var options = FormatterOptions .FromSettings(Settings.Default) .Set(o => { o.TabSize = textDocument.TabSize; o.IgnoreTokens = CodeCommentHelper .GetTaskListTokens(_package) .Concat(Settings.Default.Formatting_IgnoreLinesStartingWith.Cast <string>()) .ToArray(); }); while (start.Line <= end.Line) { if (CodeCommentHelper.IsCommentLine(start)) { var comment = new CodeComment(start, options); if (comment.IsValid) { comment.Format(); foundComments = true; } if (comment.EndPoint != null) { start = comment.EndPoint.CreateEditPoint(); } } if (start.Line == textDocument.EndPoint.Line) { break; } start.LineDown(); start.StartOfLine(); } return(foundComments); }
private void CreateStructureClass(Structure structure) { string className = structure.Name.UpperCaseFirstCharacter(); Console.WriteLine($"Processing structure class {structure.Name} [{className}()]"); CreateCompileUnit(out CodeCompileUnit compileUnit, out CodeNamespace codeNamespace, _ezspStructurePackage); CodeTypeDeclaration protocolClass = new CodeTypeDeclaration(className) { IsClass = true, TypeAttributes = System.Reflection.TypeAttributes.Public }; AddNamespaceImport(codeNamespace, _serializerPackage); StringBuilder descriptionStringBuilder = new StringBuilder(); descriptionStringBuilder.AppendLine($"Class to implement the Ember Structure \" {structure.Name} \"."); if (!string.IsNullOrEmpty(structure.Description)) { OutputWithLineBreak(descriptionStringBuilder, "", structure.Description); } ICodeCommentEntity descriptionCodeCommentEntity = new CodeCommentEntity { Tag = CodeCommentTag.Summary, DocumentationText = descriptionStringBuilder.ToString() }; CodeCommentStatement descriptionCodeComment = CodeCommentHelper.BuildCodeCommentStatement(descriptionCodeCommentEntity, true); protocolClass.Comments.Add(descriptionCodeComment); codeNamespace.Types.Add(protocolClass); CreateStructureConstructor(protocolClass); CreateStructureConstructor(protocolClass, true); CreateParameters(codeNamespace, protocolClass, structure.Parameters); CreateParameterSetters(structure.Parameters, codeNamespace, protocolClass); CreateParameterGetter(structure.Parameters, codeNamespace, protocolClass); CreateStructureSerializer(structure.Parameters, protocolClass); CreateStructureDeserializer(structure.Parameters, protocolClass); CreateToStringOverride(className, structure.Parameters, protocolClass); GenerateCode(compileUnit, className, "Structure/"); }
/// <summary> /// Helper function to generate the preview in the options menu. /// </summary> public static string FormatXml(string text, string prefix = "///") { var xml = XElement.Parse($"<doc>{text}</doc>"); var formatter = new CommentFormatter( new CommentLineXml(xml), new FormatterOptions { IgnoreTokens = new[] { "TODO: " }, TabSize = 4 }, new CommentOptions { Prefix = prefix, Regex = CodeCommentHelper.GetCommentRegex(CodeLanguage.CSharp, !string.IsNullOrWhiteSpace(prefix)) }); return(formatter.ToString()); }
/// <summary> /// Gets a starting point adjusted for leading comments. /// </summary> /// <param name="originalPoint">The original point.</param> /// <returns>The adjusted starting point.</returns> private static EditPoint GetStartPointAdjustedForComments(TextPoint originalPoint) { var commentPrefix = CodeCommentHelper.GetCommentPrefix(originalPoint.Parent); var point = originalPoint.CreateEditPoint(); while (point.Line > 1) { string text = point.GetLines(point.Line - 1, point.Line); if (RegexNullSafe.IsMatch(text, @"^\s*" + commentPrefix)) { point.LineUp(); point.StartOfLine(); } else { break; } } return(point); }
/// <summary> /// Helper function to generate the preview in the options menu. /// </summary> public static string Format(string text, string prefix = null, Action <FormatterOptions> options = null) { var xml = XElement.Parse($"<doc>{text}</doc>"); var formatterOptions = FormatterOptions .FromSettings(Properties.Settings.Default) .Set(o => o.IgnoreTokens = new[] { "TODO: " }); options?.Invoke(formatterOptions); var commentOptions = new CommentOptions { Prefix = prefix, Regex = CodeCommentHelper.GetCommentRegex(CodeLanguage.CSharp, !string.IsNullOrWhiteSpace(prefix)) }; var formatter = new CommentFormatter( new CommentLineXml(xml, formatterOptions), formatterOptions, commentOptions); return(formatter.ToString()); }
private EditPoint Expand(TextPoint point, Action <EditPoint> foundAction) { EditPoint i = point.CreateEditPoint(); EditPoint result = null; do { var line = i.Line; var text = i.GetLine(); if (CodeCommentHelper.LineMatchesRegex(i, _commentLineRegex).Success) { result = i.CreateEditPoint(); foundAction(i); // If result and iterator line are the same, the found action (move line up or // down) did nothing. This means there is no point to keep searching, it would // create an infinite loop. if (result.Line == i.Line) { break; } } else { if (i != null && result != null && CodeCommentHelper.LineMatchesRegex(i, _codeLineRegex).Success) { result = null; } i = null; } } while (i != null); return(result); }
private void ParseChildNodes(XElement xml, CodeCommentOptions options) { if (string.Equals(TagName, "code", StringComparison.OrdinalIgnoreCase)) { // Content of code element should be read literally and preserve whitespace. using (var reader = xml.CreateReader()) { reader.MoveToContent(); Content = reader.ReadInnerXml(); } } else { // Loop and parse all child nodes. var node = xml.FirstNode; while (node != null) { // If the node is a sub-element, it needs to be handled seperately. if (node.NodeType == XmlNodeType.Element) { var e = (XElement)node; // All root level elements and certain special sub elements always need to // be on their own line. if (e.Parent == null || e.Parent.Parent == null || NewLineElementNames.Contains(e.Name.LocalName, StringComparer.OrdinalIgnoreCase)) { CloseInnerText(); Lines.Add(new CommentLineXml(e, options)); } else { // If the tag is not forced to be on it's own line, append it to the // current content as string. _innerText.Append(CodeCommentHelper.CreateXmlOpenTag(e, options)); if (!e.IsEmpty) { if (options.XmlSpaceTagContent) { _innerText.Append(CodeCommentHelper.Spacer); } ParseChildNodes(e, options); _innerText.Append(CodeCommentHelper.CreateXmlCloseTag(e, options)); } _innerText.Append(CodeCommentHelper.Spacer); } } else { // Always trim trailing var value = node.ToString().TrimEnd(CodeCommentHelper.Spacer); // If the parent is an element, trim the starting spaces. if (node.PreviousNode == null && node.Parent.NodeType == XmlNodeType.Element && !options.XmlSpaceTagContent) { value = value.TrimStart(CodeCommentHelper.Spacer); } _innerText.Append(value); // Add spacing after (almost) each word. if (node.NextNode != null || node.Parent.NodeType != XmlNodeType.Element || options.XmlSpaceTagContent) { _innerText.Append(CodeCommentHelper.Spacer); } } node = node.NextNode; } } }
/// <summary> /// Initializes a new instance of the <see cref="CodeCommentOptions" /> class. /// </summary> /// <param name="settings">The settings container.</param> /// <param name="package">The hosting package.</param> /// <param name="document">The text document.</param> public CodeCommentOptions(Settings settings, CodeMaidPackage package, TextDocument document) : this(settings, CodeCommentHelper.GetTabSize(package, document)) { }
private void ParseChildNodes(XElement xml) { if (string.Equals(TagName, "code", StringComparison.OrdinalIgnoreCase)) { // Content of code element should be read literally and preserve whitespace. using (var reader = xml.CreateReader()) { reader.MoveToContent(); Content = reader.ReadInnerXml(); } } else { // Loop and parse all child nodes. var node = xml.FirstNode; while (node != null) { // If the node is a sub-element, it needs to be handled seperately. if (node.NodeType == XmlNodeType.Element) { var e = (XElement)node; if (ShouldBeNewLine(e)) { CloseInnerText(); Lines.Add(new CommentLineXml(e)); } else { // If the tag is not forced to be on it's own line, append it to the // current content as string. _innerText.Append(CodeCommentHelper.CreateXmlOpenTag(e)); if (!e.IsEmpty) { if (Settings.Default.Formatting_CommentXmlSpaceTags) { _innerText.Append(CodeCommentHelper.Spacer); } ParseChildNodes(e); _innerText.Append(CodeCommentHelper.CreateXmlCloseTag(e)); } } } else { // Always trim trailing var value = node.ToString().TrimEnd(CodeCommentHelper.Spacer); // If the parent is an element, trim the starting spaces. if (node.PreviousNode == null && node.Parent.NodeType == XmlNodeType.Element && !Settings.Default.Formatting_CommentXmlSpaceTags) { value = value.TrimStart(CodeCommentHelper.Spacer); } // If the previous node was an XML element, put a space before the text // unless the first character is interpunction. if (node.PreviousNode != null && node.PreviousNode.NodeType == XmlNodeType.Element) { if (!StartsWithInterpunction(value)) { _innerText.Append(CodeCommentHelper.Spacer); } } _innerText.Append(value); // Add spacing after (almost) each word. if (node.NextNode != null || node.Parent.NodeType != XmlNodeType.Element || Settings.Default.Formatting_CommentXmlSpaceTags) { _innerText.Append(CodeCommentHelper.Spacer); } } node = node.NextNode; } } }
private void Append(string value) { _builder.Append(_options.XmlKeepTagsTogether ? CodeCommentHelper.FakeToSpace(value) : value); _currentPosition += WordLength(value); _isFirstWord = false; }
/// <returns> /// Returns <c>true</c> if the line requests a break afterwards (did not fit on a single /// line), otherwise <c>false</c>. /// </returns> private bool FormatXml(CommentLineXml xml) { var isLiteralContent = !string.IsNullOrEmpty(xml.Content); var split = xml.TagOptions.Split; if (isLiteralContent) { // Tags containing literal content with multiple with should always be on their own line. if (xml.Content.Contains('\n')) { split = XmlTagNewLine.Always; } } else if ((split == XmlTagNewLine.Default || split == XmlTagNewLine.Content) && xml.Lines.Count > 1) { // Split always if there is more than one child line. split = XmlTagNewLine.Always; } if (split.HasFlag(XmlTagNewLine.BeforeOpen) && !_isFirstWord) { NewLine(); } Append(xml.TagOptions.KeepTogether ? CodeCommentHelper.FakeToSpace(xml.OpenTag) : xml.OpenTag); // Self closing tags have no content, skip all further logic and just output. if (xml.IsSelfClosing) { if (split.HasFlag(XmlTagNewLine.AfterClose)) { if (!xml.IsLast) { NewLine(); } return(false); } return(true); } if (split.HasFlag(XmlTagNewLine.AfterOpen)) { NewLine(); } // Increase the indenting. _indentAmount += xml.TagOptions.Indent; if (isLiteralContent) { // If the literal content of an XML tag is set, output that content without formatting. var literals = xml.Content.Trim('\r', '\n').TrimEnd('\r', '\n', '\t', ' ').Split('\n'); for (int i = 0; i < literals.Length; i++) { if (i > 0) { NewLine(true); } Append(literals[i].TrimEnd(), true); } } else { // Else output the child lines. var xmlTagLength = WordLength(xml.OpenTag) + WordLength(xml.CloseTag) + (xml.TagOptions.SpaceContent ? 2 : 0); foreach (var line in xml.Lines) { if (!Format(line, xmlTagLength, xml.TagOptions.SpaceContent)) { split |= XmlTagNewLine.BeforeClose | XmlTagNewLine.AfterClose; } } } // Remove the indenting. _indentAmount -= xml.TagOptions.Indent; // If opening tag was on own line, do the same for the closing tag. if (split.HasFlag(XmlTagNewLine.BeforeClose)) { NewLine(); } else if (xml.TagOptions.SpaceContent) { Append(CodeCommentHelper.Spacer); } Append(xml.CloseTag); if (split.HasFlag(XmlTagNewLine.AfterClose)) { //if (!xml.IsLast) { NewLine(); } return(false); } return(true); }
/// <summary> /// Parse a comment line into individual words and write it to the buffer. /// </summary> /// <param name="line">The comment line.</param> /// <param name="xmlTagLength"> /// The length of the enclosing XML tags, this is needed to calculate the line length for /// single line XML comments. /// </param> /// <param name="xmlSpaceParentTagContent"> /// Set to <c>true</c> when parent is an XML tag and wants space between tags and content. /// </param> /// <returns> /// <c>true</c> if line fitted on single line, <c>false</c> if it wrapped on multiple lines. /// </returns> private bool Format(ICommentLine line, int xmlTagLength = 0, bool xmlSpaceParentTagContent = false) { if (line is CommentLineXml xml) { return(FormatXml(xml)); } if (line.Content == null) { return(true); } var matches = _commentOptions.Regex.Matches(line.Content).OfType <Match>().Select(x => new CodeCommentMatch(x, _formatterOptions)).ToList(); // Remove empty matches from the start and end of the comment. CodeCommentMatch m; while (((m = matches.FirstOrDefault()) != null && m.IsEmpty) || ((m = matches.LastOrDefault()) != null && m.IsEmpty)) { matches.Remove(m); } // Join the comment matches into single lines where possible. if (matches.Count > 1) { int i = 0; do { m = matches[i]; if (m.TryAppend(matches[i + 1])) { matches.RemoveAt(i + 1); } else { i++; } } while (i < matches.Count - 1); } // Extended logic for line breaks. // - Break if there is more than 1 line match (eg. due to a list or child xml tags). // - Break if the content does not fit on a single line. var matchCount = matches.Count; var forceBreak = matchCount > 1; var fittedOnLine = true; if (!forceBreak && matchCount == 1 && matches[0].Words.Any()) { // Calculate the length of the first line. var firstLineLength = _commentPrefixLength + xmlTagLength + matches[0].Length + _indentAmount; // If set to skip wrapping on the last word, the last word's length does not matter. if (_formatterOptions.SkipWrapOnLastWord) { firstLineLength -= WordLength(matches[0].Words.Last()) + 1; } forceBreak = firstLineLength > _formatterOptions.WrapColumn; } if (_currentPosition == 0 || (!_isFirstWord && forceBreak)) { NewLine(); fittedOnLine = false; } else if (!_isFirstWord && xmlSpaceParentTagContent) { // Parent is XML tag and wants space between tags and content. Append(CodeCommentHelper.Spacer); } // Always consider the word after the opening tag as the first word to prevent an extra // space before. _isFirstWord = true; foreach (var match in matches) { if (match.IsLiteral || match.IsList) { if (!_isFirstWord) { NewLine(); fittedOnLine = false; } } if (match.IsList) { Append(match.ListPrefix); // List items include their spacing and do not require additional space, thus we // are logically still on the first word. _isFirstWord = true; } if (!match.IsEmpty) { var wordCount = match.Words.Count - 1; for (int i = 0; i <= wordCount; i++) { var word = match.Words[i]; var length = WordLength(word); var wrap = false; // If current position plus word length exceeds the maximum comment length, // wrap to the next line. Take care not to wrap on the first word, otherwise // a word that never fits a line (ie. too long) would cause endless linewrapping. if (!_isFirstWord && _currentPosition + length + 1 > _formatterOptions.WrapColumn) { wrap = true; } // If this is the last word and user selected to not wrap on the last word, // don't wrap. if (wrap && i == wordCount && _formatterOptions.SkipWrapOnLastWord) { wrap = false; } if (wrap) { NewLine(); fittedOnLine = false; // If linewrap is on a list item, add extra spacing to align the text // with the previous line. if (match.IsList) { Append(string.Empty.PadLeft(WordLength(match.ListPrefix), CodeCommentHelper.Spacer)); // Unset the first-word flag, because this is just padding and not a // proper word. _isFirstWord = true; } } else if (!_isFirstWord) { Append(CodeCommentHelper.Spacer); } Append(CodeCommentHelper.FakeToSpace(word)); } } else { // Line without words, create a blank line. First end the current line. NewLine(); // And then force a newline creating an empty one. NewLine(true); fittedOnLine = false; } } return(fittedOnLine); }