Example #1
0
        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            // Verify the current thread is the UI thread.
            ThreadHelper.ThrowIfNotOnUIThread();

            // This command will only work when there's an active document to examine.
            if (FormatCommentCommand.environment.ActiveDocument == null)
            {
                return;
            }

            // Get the selected text from the environment.
            TextSelection selection = FormatCommentCommand.environment.ActiveDocument.Selection as TextSelection;

            // Get the start end points (round down and up to the start of a line).
            EditPoint startPoint = selection.AnchorPoint.CreateEditPoint();

            startPoint.StartOfLine();

            // The initial endpoint is one line below the start.
            EditPoint endPoint = selection.ActivePoint.CreateEditPoint();

            endPoint.StartOfLine();
            endPoint.LineDown(1);

            // If nothing is selected, then figure out what needs to be formatted by the start point up and the end point down.  As long as we
            // recognize a comment line we'll keep expanding the selection in both directions.
            if (selection.IsEmpty)
            {
                // Find the start of the block.
                while (!startPoint.AtStartOfDocument)
                {
                    if (!FormatCommentCommand.IsCommentLine(startPoint))
                    {
                        startPoint.LineDown(1);
                        break;
                    }

                    startPoint.LineUp(1);
                }

                // Find the end of the block.
                while (!endPoint.AtEndOfDocument)
                {
                    if (!FormatCommentCommand.IsCommentLine(endPoint))
                    {
                        break;
                    }

                    endPoint.LineDown(1);
                }
            }

            // This will swap the old comment for the new right-margin justified and beautified comment.
            startPoint.ReplaceText(
                endPoint,
                FormatCommentCommand.FormatCommentstring(startPoint.GetText(endPoint)),
                (int)(vsEPReplaceTextOptions.vsEPReplaceTextNormalizeNewlines | vsEPReplaceTextOptions.vsEPReplaceTextTabsSpaces));
        }
Example #2
0
        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        /// <returns>An awaitable task.</returns>
        public static async Task InitializeAsync(AsyncPackage package)
        {
            // Verify the current thread is the UI thread.
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            // Instantiate the command.
            OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;

            FormatCommentCommand.instance = new FormatCommentCommand(package, commandService);
        }
Example #3
0
        /// <summary>
        /// Beatifies XML comments.
        /// </summary>
        /// <param name="outputComment">The output comment.</param>
        /// <param name="inputComment">The comment to be wrapped and beautified.</param>
        /// <param name="prefix">The current comment line's prefix.</param>
        private static void WrapXml(StringBuilder outputComment, string inputComment, string prefix)
        {
            // The main idea around formatting XML is to put tags on their own lines when the text can't fit within the margin.  To accomplish this,
            // we'll format the entire tag and then see how long is it.
            StringBuilder commentBody = new StringBuilder();

            FormatCommentCommand.WrapLine(commentBody, inputComment, prefix);

            // This will pull apart the XML into the tags and the content.
            Match  match    = FormatCommentCommand.xmlParts.Match(inputComment);
            string startTag = match.Groups["startTag"].Value;
            string body     = match.Groups["body"].Value;
            string endTag   = match.Groups["endTag"].Value;

            // Extract the element name.  The name will be used to determine if the tags appear on their own comment lines or if they can be
            // collapsed onto a single line with their comment.  For example, the ' <summary>' tag is generally emitted on its own line whereas the '
            // <param>' tag is usually combined with the comments.
            Regex  xmlElementName = new Regex(@"<(?<name>\w+).*>");
            string elementName    = xmlElementName.Match(startTag).Groups["name"].Value;

            // If the tags and the content is too long to fit on a line after formatting, we'll put the tags on their own lines.  We also force a
            // certain class of tags to appear on their own line distinct from the content; it's just easier to read this way.
            if (commentBody.Length >= Properties.Settings.Default.WrapMargin || FormatCommentCommand.breakingElements.Contains(elementName))
            {
                // Write the start tag.
                outputComment.Append(prefix);
                outputComment.Append(' ');
                outputComment.Append(startTag);
                outputComment.AppendLine();

                // This will format the body of the tag to wrap at the right margin.
                FormatCommentCommand.columnPosition = 0;
                FormatCommentCommand.WrapLine(outputComment, body, prefix);
                if (FormatCommentCommand.columnPosition != 0)
                {
                    outputComment.AppendLine();
                }

                // Write the closing tag.
                outputComment.Append(prefix);
                outputComment.Append(' ');
                outputComment.Append(endTag);
                outputComment.AppendLine();
            }
            else
            {
                // If the XML tags and the line fit within the margin, we just write it to the output comment.
                outputComment.Append(commentBody.ToString());
                outputComment.AppendLine();
            }

            // Reset the wrapping parameters for the next comment line in the block.
            FormatCommentCommand.columnPosition = 0;
        }
        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.</param>
        /// <param name="progress">A provider for progress updates.</param>
        /// <returns>A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.</returns>
        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress <ServiceProgressData> progress)
        {
            // When initialized asynchronously, the current thread may be a background thread at this point.
            // Do any initialization that requires the UI thread after switching to the UI thread.
            await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            await FormatCommentCommand.InitializeAsync(this);

            await FormatXmlCommand.InitializeAsync(this);

            await InsertModuleHeaderCommand.InitializeAsync(this);

            await InsertConstructorHeaderCommand.InitializeAsync(this);

            await SetModuleHeaderCommand.InitializeAsync(this);

            await SetWrapMarginCommand.InitializeAsync(this);

            await ScrubXsdCommand.InitializeAsync(this);
        }
Example #5
0
        /// <summary>
        /// Formats a block of comments and beautifies the XML comments.
        /// </summary>
        /// <param name="inputComment">The block of comment lines to format.</param>
        /// <returns>A right justified and beautified block of comments.</returns>
        private static string FormatCommentstring(string inputComment)
        {
            // These variables control the state of the wrapping.
            FormatCommentCommand.columnPosition = 0;

            // This buffer keeps track of the reconstructed comment block.
            StringBuilder outputComment = new StringBuilder();

            // This breaks the input comment into individual lines.
            string[] commentLines = inputComment.Split(new string[] { "\r\n" }, StringSplitOptions.None | StringSplitOptions.RemoveEmptyEntries);

            // Parse the line into prefixes (the comment start sequence and whitespace) and the actual comment on the line.
            for (int index = 0; index < commentLines.Length; index++)
            {
                // This parses the comment and the prefix out of the current comment line.  The prefix is all characters up to the comment delimiter
                // character.  The comment represents all the characters after the delimiter stripped of the leading and trailing space.
                Match  match   = FormatCommentCommand.commentRegex.Match(commentLines[index]);
                string prefix  = match.Groups["prefix"].Value;
                string comment = match.Groups["comment"].Value;

                // We assume there was some intent if an author left an entire line blank and so we treat it as a paragraph break.  That is, we don't
                // wrap over it.
                if (comment.Length == 0)
                {
                    if (FormatCommentCommand.columnPosition != 0)
                    {
                        outputComment.AppendLine();
                    }

                    outputComment.AppendLine(prefix);
                    FormatCommentCommand.columnPosition = 0;
                    continue;
                }

                // We're going to attempt to format the special comment directives so that they'll wrap nicely.  If a single-line directive is too
                // long, we'll wrap it so the directives end up on separate lines.  This test is used to distinguish between the special directives
                // used for commenting functions, classes and modules from regular block comments.
                bool isCommentDirective = prefix.EndsWith("///", StringComparison.Ordinal);

                // This section will provide formatting for XML tags inside comment blocks.  The tags are examined to see if the text and tags will
                // fit within the margins.  If not, the tags are placed on their own lines and the comment inside the tags is formatted to wrap
                // around the margins.  The algorithm will also eat up partial lines in order to fill out the XML content to the margin.
                if (isCommentDirective && xmlStartTag.IsMatch(comment) && !xmlEmptyElement.IsMatch(comment))
                {
                    while (!xmlEndTag.IsMatch(comment) && index < commentLines.Length)
                    {
                        match    = FormatCommentCommand.commentRegex.Match(commentLines[++index]);
                        comment += ' ' + match.Groups["comment"].Value;
                    }

                    FormatCommentCommand.WrapXml(outputComment, comment, prefix);
                    continue;
                }

                // This is used to force a line break on comment lines that meet certain criteria, such as bullet marks and examples.
                bool isBreakingLine = false;

                // Lines that begin with an Asterisk are considered bullet marks and do not wrap and are given an extra margin.
                if (comment.StartsWith("*", StringComparison.Ordinal))
                {
                    // If the previous line was waiting for some wrapping to occur, it's going to be disappointed.  This line is going to start all
                    // on its own.
                    if (FormatCommentCommand.columnPosition != 0)
                    {
                        outputComment.AppendLine();
                    }

                    FormatCommentCommand.columnPosition = 0;

                    // The prefix will be indented for all bullet marks.
                    prefix += "    ";

                    // This will force a line break after the bullet mark is formatted.
                    isBreakingLine = true;
                }

                // Lines that end with colons do not wrap.
                if (comment.EndsWith(":", StringComparison.Ordinal))
                {
                    isBreakingLine = true;
                }

                // This is where all the work is done to right justify the block comment to the margin.
                FormatCommentCommand.WrapLine(outputComment, comment, prefix);

                // This will force a new line for comment lines that don't wrap such as bullet marks and colons.
                if (isBreakingLine)
                {
                    outputComment.AppendLine();
                    FormatCommentCommand.columnPosition = 0;
                }
            }

            // This will finish off any incomplete lines.
            if (FormatCommentCommand.columnPosition > 0)
            {
                outputComment.AppendLine();
            }

            // At this point we've transformed the input block of comments into a right justified block.
            return(outputComment.ToString());
        }