• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 413
  • Last Modified:

How can I programmatically comment out selected text in a RichTextBox control?

How can I comment out the selected text in a RichTextBox when it spans multiple lines?

All lines with a selection or partial selection should be commented, but lines in the selection with no text should be ignored.  Any partial selection should be extended to the whole line.

Assume the standard C# comment characters '//' for the moment.

Chris Bray
0
chrisbray
Asked:
chrisbray
  • 6
  • 4
  • 2
  • +1
2 Solutions
 
Miguel OzSoftware EngineerCommented:
I will check richTextBox1.SelectedText  property as follows:
string[] tempArray = richTextBox1.Lines;

    // Loop through the array and comment required lines.
    for(int counter=0; counter < tempArray.Length;counter++)
    {
       if (richTextBox1.SelectedText.Contains(tempArray[counter])
      {
            tempArray[counter] = "//" + tempArray[counter];
       }
    }

richTextBox1.Lines = tempArray;


0
 
anarki_jimbelCommented:
Try this code. Not very clean but gives an idea. Honestly, I wouldn't rely on SelectedText.Contains as above...


        private void button2_Click(object sender, EventArgs e)
        {
            int selStart = this.richTextBox1.SelectionStart;
            int selEnd = this.richTextBox1.SelectionStart + richTextBox1.SelectedText.Length;
            int newSelStart = -1;
            int newSelEnd = -1;
            int textLengthSofar = 0;
            string newText = "";
            bool endSet = false;

            for (int i = 0; i < richTextBox1.Lines.Length; i++)
            {


                textLengthSofar += richTextBox1.Lines[i].Length + 1;
                if (selStart < textLengthSofar && newSelStart==-1)
                {
                    //selStartLine = i;
                    newSelStart = newText.Length;
                }
                if (selEnd <= textLengthSofar && newSelEnd == -1)
                {
                    //selEndLine = i;
                    newSelEnd = textLengthSofar;

                }
                if (newSelStart != -1 && newSelEnd == -1 && ! endSet && richTextBox1.Lines[i].Length > 0)
                {
                    newText += "//" + richTextBox1.Lines[i] + "\n";
                }
                else if (newSelStart != -1 && !endSet && richTextBox1.Lines[i].Length > 0)
                {
                    newText += "//" + richTextBox1.Lines[i] + "\n";
                    endSet = true;
                }
                else
                {
                    newText += richTextBox1.Lines[i] + "\n";
                }


            }

            richTextBox1.Text = newText;
            richTextBox1.Select(newSelStart, newSelEnd - newSelStart);

        }

Open in new window

0
 
chrisbrayAuthor Commented:
Hi anarki_jimbel:

Thank you for your prompt response.  Your solution nearly works - the final selection is incorrect because it does not include the length of the comment string, which is easy to fix.

However, it also appends the comments at the beginning of the line whereas it ought to ignore whitespace (spaces, tabs, etc.) and insert the comment markers in front of the first piece of leftmost text and then all other comment markers should line up underneath it - I am trying to achieve the same result as in Visual Studio.

Also, I had in mind passing out the selection to a separate 'AddComments' method to allow it to be used with any text based control rather than just a single specified RichTextBox - any suggestions for this?

Chris Bray
0
Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.

 
Jon500Commented:
Hi Chris,

I wrote a function called "commentLine". If you have a string, just pass it to this function and it will add the const comment symbol COMMENT_TEXT to the string after the leading whitespace (I used "\\ " [noticed the space after for formatting/aesthetic appeal). To use the function, revise the two lines in anarki's fine example with:

newText += commentLine(richTextBox1.Lines[i]) + "\n";

Regards,
Jon500
private string commentLine(string s)
    {
       const string COMMENT_TEXT = @"\\ ";
       string prefix = "";
       string suffix = "";

       Match match = Regex.Match(s, @"^\s*");
       if (match.Success)
       {
          prefix = match.Groups[0].Value;
       }

       if (s.Length > prefix.Length)
       {
          suffix = s.Substring(prefix.Length);
       }

       return prefix + COMMENT_TEXT + suffix;
    }

Open in new window

0
 
anarki_jimbelCommented:
Yeah, you are right with selection.
What about other stuff - I believe it can be fixed in my code. Unfortunately, I din't know VS solution :)
0
 
chrisbrayAuthor Commented:
Thanks guys for your contributions.

There are a few other issues to address to get it absolutely right (we need to add the length of the '\n' entries in each line to the final length of the selection, and delete the last one to avoid an extra row being added for example).

I intend to pass the lines to an array, to avoid looping the lines property and use a Stringbuilder instead of a string to avoid immutability.  I will post the final solution once it is done for the benefit of others.
0
 
chrisbrayAuthor Commented:
Hi Guys,

I still want to improve on this, and make it more portable.  However, using the provided examples and my own additions I have cobbled together a working system for commenting and uncommenting selected code in a specified Rich Text Box.

I hope it helps someone else who is trying to achieve this, and would welcome any further suggestions for improvements.

Chris Bray



    public static class CommentStrings
    {
        #region Constants

        public const string CSharp = @"//";
        public const string Slashes = CSharp;
        public const string SQL = @"--";
        public const string Dashes = SQL;
        public const string VB = "'";
        public const string SemiColon = ";";

        // Add more if you wish...

        #endregion
    }

        public static string CommentLine(string input, string commentToken)
        {
            string prefix = "";
            string suffix = "";

            Match match = Regex.Match(input, @"^\s*");
            if (match.Success)
            {
                prefix = match.Groups[0].Value;
            }

            if (input.Length > prefix.Length)
            {
                suffix = input.Substring(prefix.Length);
            }

            return string.Format("{0}{1}{2}", prefix, commentToken, suffix);
        }

        public static string UncommentLine(string input, string commentToken)
        {
            string prefix = "";
            string suffix = "";

            string key = string.Format(@"^\s*{0}", commentToken);
            Match match = Regex.Match(input, key);
            if (match.Success)
            {
                prefix = match.Groups[0].Value;
            }

            if (input.Length > prefix.Length)
            {
                suffix = input.Substring(prefix.Length);
            }

            prefix = Regex.Replace(prefix, commentToken, string.Empty);

            return string.Format("{0}{1}", prefix, suffix);

        }

        internal void CommentSelection()
        {
            string commentString = CommentStrings.SQL;

            int selStart = this.richTextBoxInput.SelectionStart;
            int selEnd = this.richTextBoxInput.SelectionStart + richTextBoxInput.SelectedText.Length;
            int newSelStart = -1;
            int newSelEnd = -1;
            int textLengthSofar = 0;
            StringBuilder builder = new StringBuilder();
            bool endSet = false;

            for (int i = 0; i < richTextBoxInput.Lines.Length; i++)
            {
                textLengthSofar += richTextBoxInput.Lines[i].Length + 1;

                if (selStart < textLengthSofar && newSelStart == -1)
                {
                    newSelStart = builder.Length;
                }

                if (selEnd <= textLengthSofar && newSelEnd == -1)
                {
                    newSelEnd = textLengthSofar;
                }

                if (newSelStart != -1 && newSelEnd == -1 && !endSet && richTextBoxInput.Lines[i].Length > 0)
                {
                    builder.AppendFormat("{0}{1}", StringExtensions.CommentLine(richTextBoxInput.Lines[i], commentString), "\n");
                }
                else if (newSelStart != -1 && !endSet && richTextBoxInput.Lines[i].Length > 0)
                {
                    builder.AppendFormat("{0}{1}", StringExtensions.CommentLine(richTextBoxInput.Lines[i], commentString), "\n");
                    endSet = true;
                }
                else
                {
                    builder.Append(richTextBoxInput.Lines[i] + "\n");
                }
            }

            // Remove the closing new line
            builder.Remove(builder.Length - 1, 1);


            richTextBoxInput.Text = builder.ToString();
            richTextBoxInput.Select(newSelStart, newSelEnd - newSelStart + commentString.Length + richTextBoxInput.Lines.Length);

        }

        internal void UncommentSelection()
        {
            string commentString = CommentStrings.SQL;

            int selStart = this.richTextBoxInput.SelectionStart;
            int selEnd = this.richTextBoxInput.SelectionStart + richTextBoxInput.SelectedText.Length;
            int newSelStart = -1;
            int newSelEnd = -1;
            int textLengthSofar = 0;
            StringBuilder builder = new StringBuilder();
            bool endSet = false;

            for (int i = 0; i < richTextBoxInput.Lines.Length; i++)
            {
                textLengthSofar += richTextBoxInput.Lines[i].Length + 1;

                if (selStart < textLengthSofar && newSelStart == -1)
                {
                    newSelStart = builder.Length;
                }

                if (selEnd <= textLengthSofar && newSelEnd == -1)
                {
                    newSelEnd = textLengthSofar;
                }

                if (newSelStart != -1 && newSelEnd == -1 && !endSet && richTextBoxInput.Lines[i].Length > 0)
                {
                    //builder.Append(commentString + richTextBoxInput.Lines[i] + "\n");
                    builder.AppendFormat("{0}{1}", StringExtensions.UncommentLine(richTextBoxInput.Lines[i], commentString), "\n");
                }
                else if (newSelStart != -1 && !endSet && richTextBoxInput.Lines[i].Length > 0)
                {
                    //builder.Append(commentString + richTextBoxInput.Lines[i] + "\n");
                    builder.AppendFormat("{0}{1}", StringExtensions.UncommentLine(richTextBoxInput.Lines[i], commentString), "\n");
                    endSet = true;
                }
                else
                {
                    builder.Append(richTextBoxInput.Lines[i] + "\n");
                }
            }

            // Remove the closing new line
            builder.Remove(builder.Length - 1, 1);


            richTextBoxInput.Text = builder.ToString();
            richTextBoxInput.Select(newSelStart, newSelEnd - newSelStart - commentString.Length - richTextBoxInput.Lines.Length);
        }

Open in new window

0
 
Jon500Commented:
Nicely done.

Jon500
0
 
chrisbrayAuthor Commented:
Hi Jon,

Thanks.  Unfortunately the code does not line up the comment marks with the farthest left comment, which I would very much have liked to achieve.  The only way I can see to do it is to iterate the lines twice, first to get the leftmost position and then again to actually make the change.  However, getting the regex to deal with that is definitely beyond my current skill set :-)

I will keep researching though, and improve it when I can find an answer.

Chris Bray
0
 
Jon500Commented:
I'm not sure why you would want to line-up comment marks with the furthest left comment because the user may have specifically indented their lines before trying to comment them. If you do what you suggest, you will possibly frustrate users who will need to add whitespace to indent as things were before the comment marks were added.

If you do want to do what you said, then I agree that you would need to make 2 passes. The Regex in my CommentLine function gives you the length of the whitespace preceding code, so you can set a property value that maintains the shortest white-space length then on the second pass remove white space and add whatever that length is...

Regards,
Jon500
0
 
chrisbrayAuthor Commented:
Hi Jon,

> I'm not sure why you would want to line-up comment marks with the furthest left comment

I want to do that because it is the default behaviour of Visual Studio in particular and most other commenting capable applications.  Try it for yourself in VS and you should see what I mean ot should come out like the example below where  the comment token is inserted part way through the whitespace so that the user indentation the have so carefully crafted is fully maintained.  Uncommenting should reverse the procedure.

Does that make sense?

                using (IDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    //while (reader.Read())
                    //{
                    //    long nameId = reader.GetInt64(0);
                    //    string accountName = reader.GetString(1);
                    //    int linkTypeId = reader.GetInt32(2);
                    //    string id = string.Format("{0}:{1}", linkTypeId, nameId);
                    //    long taxCodeId = reader.GetInt64(3);
                    //    payeeList.Add(new Payee(id, accountName, taxCodeId));
                    //}            
                }

Open in new window

0
 
Jon500Commented:
OK--I think I misunderstood. I thought you wanted the LINES to line-up too but you want just the comment marks to line up. As I wrote before I think you will need two passes and this would be a nice touch.

Regards,
Jon500
0
 
chrisbrayAuthor Commented:
Hi Jon,

Indeed. Have you any suggestion for locating the position at which the comment should be inserted, and for inserting it? I guess we would need to split the prefix at the appropriate place, but cannot see how to do it with the regex.

Chris Bray
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

  • 6
  • 4
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now