Inserting and removing block comments with keeping indents by shortcut

Inserting and removing block comments with keeping indents by shortcut

3

    Nov 11, 2012#1

    CSS does not have a line comment. But I want to use the same shortcut key to comment selected lines. I want UltraEdit to insert /* at the beginning of the first line selected and */ at the end of the last line selected.

    I know that the commands Comment Add and Comment Remove cannot be used as they work only with line comments.

    I also know that there are in menu Edit at bottom the commands Comment Selection and Uncomment Selection to insert/remove a block comment on selected text since UltraEdit v16.00.

    There is also a macro solution posted at macro to comment block of code and a template solution for adding the block comment strings as posted at CSS add/remove comment button greyed out.

    But I don't know how to write a macro that will let me select parts of a line and inserts the /* and */ in the right place.

    Code: Select all

         the<BeginSelection>first line
         the second line
         the third<EndSelection>line

    Code: Select all

    /*  the first line
        the second line
        the third line */
    I also want to start the selection anywhere in line 1 and end the selection anywhere in line 3, press the comment shortcut key to remove the /* and */.

    Anyone know how to do this?

    6,603548
    Grand MasterGrand Master
    6,603548

      Nov 13, 2012#2

      In my point of view it is stupid to select parts of a line when entire lines should be commented.

      I have nevertheless thought about a macro to get what you want, but there are lots of steps necessary which temporarily modify the file and therefore produces lots of undo steps because of the lack of variables in macro environment.

      Would it be acceptable for you to use an UltraEdit script instead of a macro for this task?

      With a script it would be possible to solve this request with just 1 modification of the file including keeping first line perfectly indented after comment/uncomment.

      I just need to know for perfect indentation of first line of block what is the indent value with spaces used for the indents respectively the tab stop value if tabs are used. From the example it looks like 5 is the indent value, but I doubt on that value.

      3

        Nov 13, 2012#3

        Yes, a script would be fine. The indentation can be a variable number of spaces or tabs.

        6,603548
        Grand MasterGrand Master
        6,603548

          Nov 16, 2012#4

          Developing the script below was really a hard work. Mixed spaces/tab are awful. I just can hope to have all possible cases correct covered by appropriate code.

          You have to set the values of the two variables nTabStopValue and bUseSpaces to the values configured at Advanced - Configuration - Editor - Word Wrap / Tab Settings for CSS files. I suppose that tab stop value and indent value are configured identical.

          Code: Select all

          // This function calculates column number with taking horizontal
          // tabs and the tab stop value as defined below into account.
          function NextTabStop (nColumn) {
             nColumn += nTabStopValue;
             nColumn -= nColumn % nTabStopValue;
             return nColumn;
          }
          
          var nTabStopValue = 3;   // Set these 2 values according to tab settings in the configuration.
          var bUseSpaces = false;  // The number of indent spaces should be identical to tab stop value.
          
          // The tab stop value must be greater than 0.
          if (nTabStopValue < 1) nTabStopValue = 8;
          
          // Is at least 1 file opened?
          if (UltraEdit.document.length > 0) {
          
             // Is something selected in active file?
             if (UltraEdit.activeDocument.isSel()) {
          
                UltraEdit.insertMode();
                if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
                else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();
          
                // Get this selection into a string variable.
                var sSelection = UltraEdit.activeDocument.selection;
          
                // Get all line terminators into an array of strings.
                var sLineTerms = sSelection.match(/\r\n|\n|\r/g);
          
                // The number of strings in array is by one lower the number of
                // selected lines, except the selection ends with a line termination.
                // Here is included already the subtraction by 1 required for reselection.
                var nLineCount = 0;
                if (sLineTerms != null) {
                   nLineCount = sLineTerms.length;
                   if (sSelection.search(/(\r\n|\n|\r)$/g) >= 0 ) nLineCount--;
                }
          
                // The caret can blink at end or at beginning of selection depending
                // on direction used for selecting the text: downwards (more common)
                // or upwards (rare). But line number property returns always the line
                // number at beginning of selected text independent of the caret position.
                var nLineNum = UltraEdit.activeDocument.currentLineNum;
          
                // Select the lines from first character in the first line of the initial
                // selection to last character in last line of the initial selection.
                UltraEdit.activeDocument.endSelect();
                UltraEdit.activeDocument.gotoLine(nLineNum,1);
                if (nLineCount) UltraEdit.activeDocument.gotoLineSelect(nLineNum+nLineCount,1);
                UltraEdit.activeDocument.startSelect();
                UltraEdit.activeDocument.key("END");
                UltraEdit.activeDocument.endSelect();
          
                // It could be that now there is no selection anymore in
                // case of just a single blank line was selected before.
                if (UltraEdit.activeDocument.isSel()) {
          
                   // Get this updated selection into a string variable.
                   sSelection = UltraEdit.activeDocument.selection;
          
                   // Is there a block comment at beginning of the selection?
                   if (sSelection.search(/^[ \t]*\/\*/) == 0) {
          
                      // Yes, remove the block comment from beginning and end of selection.
          
                      // Get position of first slash character. This returns usually value 0.
                      var nBeginText = sSelection.indexOf("/");
          
                      // Get preceding whitespaces into string variable for new text.
                      var sNewText = (nBeginText > 0) ? sNewText = sSelection.substring(0,nBeginText) : "";
                      // Increase index value by 2 for the two characters of /*.
                      nBeginText += 2;
          
                      // Evaluate character after comment starting string for indentation.
                      // It is possible that just a comment starting string with or without
                      // preceding whitespaces is selected which should be removed.
                      if (nBeginText < sSelection.length) {
          
                         var sNextChar = sSelection.charAt(nBeginText);
          
                         // Is the next character a space character?
                         if (sNextChar == ' ') {
          
                            // If spaces should be used for indent or the tab
                            // stop value is greater than 2, use 2 spaces for /*.
                            if (bUseSpaces || (nTabStopValue > 2)) sNewText += "  ";
          
                            // If tabs are used for indents and tab stop value is 1
                            // than replace comment starting string with two tabs.
                            else if (nTabStopValue == 1) sNewText += "\t\t";
          
                            // Otherwise the tab stop value is 2 and therefore it
                            // is needed to determine column number at beginning of
                            // comment starting string to find out if a single tab
                            // or a tab plus a space is needed for replacing /*.
                            else {
                               var nColNum = 0;
                               var nCharIndex = 0;
                               sNextChar = sSelection.charAt(nCharIndex);
                               while (sNextChar != '/') {
                                  if (sNextChar == ' ') nColNum++;
                                  else nColNum = NextTabStop(nColNum);
                                  sNextChar = sSelection.charAt(++nCharIndex);
                               }
                               if (nColNum % nTabStopValue) sNewText += "\t ";
                               else sNewText += "\t";
                            }
                         }
          
                         // Is the next character a tab character?
                         else if (sNextChar == '\t') {
          
                            if (bUseSpaces) sNewText += "  ";
                            else {
          
                               // Determine column number after /* and the tab character taking
                               // the existing preceding whitespaces into account which are kept.
                               var nColNum = 0;
                               var nCharIndex = 0;
                               sNextChar = sSelection.charAt(nCharIndex);
                               while (sNextChar != '/') {
                                  if (sNextChar == ' ') nColNum++;
                                  else nColNum = NextTabStop(nColNum);
                                  sNextChar = sSelection.charAt(++nCharIndex);
                               }
                               var nNextStop = NextTabStop(nColNum+2);
          
                               // Add 0, 1 or 2 tabs to get the line aligned as before.
                               while (true) {
                                  nColNum = NextTabStop(nColNum);
                                  if (nColNum >= nNextStop) break;
                                  else sNewText += "\t";
                               }
                            }
                         }
          
                         // Find position of last non whitespace character at
                         // end of the selection ignoring comment end string */.
                         nCharIndex = sSelection.search(/[ \t]*\*\/[ \t]*$/);
          
                         // Append to kept preceding whitespaces left to /*
                         // in first line the remaining characters between
                         // the comment string removed now because not copied.
                         if (nCharIndex < 0) nCharIndex = sSelection.length;
                         sNewText += sSelection.substring(nBeginText,nCharIndex);
          
                         // Replace the current selection with the new string.
                         UltraEdit.activeDocument.write(sNewText);
                      }
                      else UltraEdit.activeDocument.deleteText();
                   }
          
                   else { // There is no block comment starting string at beginning.
          
                      // Add the block comment at beginning and end of the selection.
          
                      // By default the first 2 characters on first line are replaced by /*.
                      var nIndentOffset = 2;
                      var sFirstChar = sSelection.charAt(0);
                      // sSecondChar is an empty string if just a single character is selected.
                      var sSecondChar = sSelection.charAt(1);
          
                      // Is the first character in first selected line a space character?
                      if (sFirstChar == ' ') {
          
                         // Is second character a tab character?
                         if (sSecondChar == '\t') {
          
                            // The space must be replaced by / and the tab is kept if the
                            // tab stop value is greater than 2, otherwise replaced by *.
                            if (nTabStopValue > 2) nIndentOffset = 1;
                         }
          
                         // Is second character not a space character?
                         else if(sSecondChar != ' ') {
          
                            // The single space character is replaced always by /*.
                            nIndentOffset = 1;
                         }
                      }
          
                      // Is the first character in first selected line a tab character?
                      if (sFirstChar == '\t') {
          
                         // Is the second character a space or tab character?
                         if ((sSecondChar == ' ') || (sSecondChar == '\t')) {
          
                            // On a tab stop value of 2, just first tab must be replaced by /*.
                            if (nTabStopValue == 2) nIndentOffset = 1;
          
                            // On a tab stop value greater 2, both characters must be kept.
                            else if (nTabStopValue > 2) nIndentOffset = 0;
          
                            // With a tab stop value of 1, both character must be replaced by /*.
                         }
                         else {
                            // The tab character is replaced by /* only
                            // if the tab stop value is lower than 3.
                            nIndentOffset = (nTabStopValue < 3) ? 1 : 0;
                         }
                      }
                      else {
                         // If first character is not a whitespace character, /*
                         // must be inserted instead of overwriting whitespaces.
                         nIndentOffset = 0;
                      }
          
                      // Build the new string and replace the current selection.
                      if (String.trimRight) sSelection = sSelection.trimRight();
                      var sNewText = "/*" + sSelection.substr(nIndentOffset) + " */";
                      UltraEdit.activeDocument.write(sNewText);
                   }
                }
             }
          }
          Please note that on some cases it is impossible for the script as is to find out what should be used for indentation on removing /*

          Two examples should demonstrate that. » is a placeholder for a tab character with a tab stop value of 2.

          h1 {
          » font-size:24pt;
          » font-weight:bold
          }

          h2 {
          » font-size:18pt;
          » font-weight:bold
          }

          With the script the entire first block for h1 and just fond-definition for h2 is commented out. The results are:

          /*h1 {
          » font-size:24pt;
          » font-weight:bold
          } */

          h2 {
          /*font-size:18pt; */
          » font-weight:bold
          }

          Now we undo adding the comments by running the script on same lines again as before. The results are:

          h1 {
          » font-size:24pt;
          » font-weight:bold
          }

          h2 {
          font-size:18pt;
          » font-weight:bold
          }

          As you can see for first example the result is the same as initial CSS block was, but on second block the result is different.

          3

            Nov 17, 2012#5

            Wow! Thanks Mofi. I didn't realize this would be so hard. I am very grateful for your work.

            Can I use the same shortcut key as the line comment, but only have the script run if line comments are not supported for the file type? Can I comment the current line even if nothing is selected?

            Thank you!

            6,603548
            Grand MasterGrand Master
            6,603548

              Nov 18, 2012#6

              lukashadden wrote:Can I use the same shortcut key as the line comment, but only have the script run if line comments are not supported for the file type?
              Yes, but with a limitation. There are the commands Comment Add to insert line comments and Comment Remove to remove line comments. This script supports both depending on presence of a block comment. So you have 2 options now:
              1. You delete the hotkey assigned to command EditCommandAdd in the Key Mapping configuration dialog. Next you add the script to the script list and assign the hotkey to the script which you have just removed from command Comment Add. Last you have to modify the script as described below.

                With this configuration the script makes block comment add/remove for files with a language not supporting line comments and only line comment add for files with a language supporting line comments. Removing line comments must be still down with Comment Remove.
              2. You remove the hotkey assignments from both internal commands. You create a copy of the script with a different name and add both scripts to the list of scripts with the hotkeys assigned before to the internal commands EditCommandAdd and EditCommandRemove. On second script for removing line comments you have to change 1 command as shown below.

                With this configuration both scripts make block comment add/remove for files with a language not supporting line comments while one script is for adding line comments and the other for removing line comments.
              lukashadden wrote:Can I comment the current line even if nothing is selected?
              Yes, this is possible, if the script is slightly changed too.

              The script posted above contains at beginning the lines:

              Code: Select all

                // Is something selected in active file?
                 if (UltraEdit.activeDocument.isSel()) {
              This command must be replaced by:

              Code: Select all

                 // If nothing selected in active file, select the current line.
                 if (!UltraEdit.activeDocument.isSel()) UltraEdit.activeDocument.selectLine();
              
                 // Does this file contain according to file extension a language supporting line comments?
                 if (!UltraEdit.activeDocument.isExt("css") && !UltraEdit.activeDocument.isExt("htm") &&
                     !UltraEdit.activeDocument.isExt("html") /* and other file extensions */ ) {
                    // Yes, use the internal command for adding a line comment.
                    UltraEdit.activeDocument.commentAdd();
                    // Yes, use the internal command for removing a line comment.
              //    UltraEdit.activeDocument.commentRemove();  //<- For the other script for removing line comments.
                 }
                 else {
                    // The file contains a language not supporting line comments.
                    // Insert or remove block comments with keeping indents.
              The problem with this approach is that a HTML file contains usually multiple languages. Some do not support line comments like CSS and HTML, others support line comments like Javascript. There is no possibility to get access by script to any syntax highlighting information like language name or comment definitions. Therefore everything must be coded in script.

              As you can see the script uses a sequence of a not case sensitive isExt function with evaluating the negative return value in an AND combination to determine which file extensions contain usually languages which definitely do not support line comments. For those files the script code is used, for all other the internal line comment add/remove commands.

              Of course for HTML code the script would not make a good job at the moment as /* and */ are the wrong strings for HTML comments. So the script must be further enhanced for other languages than CSS, C/C++/C#, Javascript, ...

              Finally you could enhance the script for making also line comment add/remove by script code instead of using UltraEdit's internal commands. It is up to you how smart you would like to code this script. The main techniques required are already documented now in the script I have posted above.