Exemplo n.º 1
0
        /// <summary>
        /// Find if the current insertion point is a comment and, if it is, insert a * on the following
        /// line at the correct indentation.
        /// </summary>
        public static void FormatMultilineComment(this ITextView textView, SnapshotPoint commentStartingPoint, SnapshotPoint insertionPoint)
        {
            // Since this was marked as a comment span (before calling this method), this method does not
            // handle being called from insertion points not in/following a multiline comment.
            bool commentStartingPointPrechecks =
                commentStartingPoint.Snapshot != null &&
                commentStartingPoint.Position + 2 <= commentStartingPoint.Snapshot.Length &&
                commentStartingPoint.Snapshot.GetText(commentStartingPoint.Position, 2) == "/*";

            bool insertionPointPrechecks =
                insertionPoint.Snapshot != null &&
                insertionPoint.Position > commentStartingPoint.Position;

            var snapshot = commentStartingPoint.Snapshot;

            Debug.Assert(commentStartingPointPrechecks,
                         "Comment Starting Point should be set to the beginning of a multiline comment.");

            Debug.Assert(insertionPointPrechecks,
                         "Insertion Point must be set to a position after the Comment Starting Point");

            // If for any reason any of the prechecks fail, just don't format the comment.  A no-op seems better than
            // an odd formatting result...
            if (!commentStartingPointPrechecks || !insertionPointPrechecks)
            {
                return;
            }

            // Figure out the amount of whitespace preceeding the * on the line.  Take spacing style with this to match.
            int    startOfFirstCommentLine = snapshot.GetLineFromPosition(commentStartingPoint).Start;
            string beforeAsterisk          = snapshot.GetText(startOfFirstCommentLine, commentStartingPoint - startOfFirstCommentLine);

            // If ConvertTabsToSpaces is enabled, do that and make a string of all spaces of the same length.
            // Otherwise, walk the string and replace all non-whitespace characters with a space and use that string.
            if (textView.Options.IsConvertTabsToSpacesEnabled())
            {
                var tabSize = textView.Options.GetTabSize();
                beforeAsterisk = String.Format("{0} *", TextHelper.ConvertTabsToSpaces(beforeAsterisk, tabSize, true));
            }
            else
            {
                // Create a string builder and make it 2 spaces bigger for the asterisk at the end.
                var sb = new StringBuilder(beforeAsterisk.Length + 2);

                // If the user wants to keep tabs, then we need to keep them in here.  Convert non-whitespace characters.
                for (int i = 0; i < beforeAsterisk.Length; i++)
                {
                    if (!char.IsWhiteSpace(beforeAsterisk[i]))
                    {
                        sb.Append(" ");
                    }
                    else
                    {
                        sb.Append(beforeAsterisk[i]);
                    }
                }

                sb.Append(" *");
                beforeAsterisk = sb.ToString();
            }

            // Calculate the amount of space following the *.  1 if there is only whitespace, indent otherwise.
            string afterAsterisk = " ";       // by default we want a single space

            if (insertionPoint.Position >= 1) // only do if we aren't the first line.
            {
                string previousLineText = snapshot.GetLineFromPosition(insertionPoint.Position - 1).GetText();

                // Replace tabs in previous line string if we have that option set so later calculations are correct.
                if (textView.Options.IsConvertTabsToSpacesEnabled())
                {
                    var tabSize = textView.Options.GetTabSize();
                    previousLineText = TextHelper.ConvertTabsToSpaces(previousLineText, tabSize);
                }

                string trimmedPreviousLine = previousLineText.Trim();
                if (trimmedPreviousLine.StartsWith("*") && trimmedPreviousLine.Length != 1)
                {
                    // if it started with the *, take the whitespace between that and the next non-whitespace character.
                    // trim the whitespace off the front so we know how much of the string was whitespace after the asterisk
                    var whitespaceStringLength = trimmedPreviousLine.Length - trimmedPreviousLine.Substring(1).TrimStart().Length - 1;
                    afterAsterisk = trimmedPreviousLine.Substring(1, whitespaceStringLength);
                }
            }

            // Find the position to insert the new line.
            int commentInsertionPoint = insertionPoint;

            // Insert the whitespace * string and set the caret to the correct position
            using (var edit = snapshot.TextBuffer.CreateEdit()) {
                edit.Insert(commentInsertionPoint, beforeAsterisk + afterAsterisk);
                edit.Apply();
            }
            var pt = textView.BufferGraph.MapUpToBuffer(
                new SnapshotPoint(snapshot.TextBuffer.CurrentSnapshot, commentInsertionPoint + beforeAsterisk.Length + afterAsterisk.Length),
                PointTrackingMode.Positive,
                PositionAffinity.Successor,
                textView.TextSnapshot.TextBuffer
                );

            if (pt != null)
            {
                // Set the cursor position immediatelly following our edit to guarantee placement.
                textView.Caret.MoveTo(pt.Value);
            }
        }