HTML comment script

s1w
2011-06-05
2013-01-25
  • s1w
    s1w
    2011-06-05

    Function of this script is to fast comment/uncomment single line without selection for Notepad++.

    import re
    line = editor.getCurLine()
    p1 = re.compile('^(.*?)((?P<lt><!--+\s*)|(?P<rt>\s*--+>))(?P<block>.*?)((?(rt)|(?(lt)((?=<!--+\s*)|\s*--+>)))|$)')
    p2 = re.compile('\r?(\s*)(.*)\s*\n')
    if '<!--' in line or '-->' in line:
        line = p1.sub(r'\1\g<block>', line).rstrip()
    else:
        line = p2.sub(r'\1<!-- \2', line).rstrip() + ' -->'
    

    + allows conditional instances of '<!-' and '->' in NON-GREEDY way
    + shortens '<!------' and '------->' cases
    + correctly handles reversed setting of comments "-> . . . <!-" and recognizes "<!- .  . <!- .  . ->" situations

     
  • s1w
    s1w
    2011-06-05

    lol, forgot about printing:

    currentLine  = editor.lineFromPosition(editor.getCurrentPos())
    editor.replaceLine(currentLine, line)
    

    s1w

     
  • s1w
    s1w
    2011-06-12

    ok, i've made "some correction". hope its final nao

    #Toggle HTML Comment >> PythonScript for Notepad++
    # -comments single line without selection/removes comment tags in NON-GREEDY or GREEDY style (see options)
    # -comments selection/clears all comments from selection
    # -intelligently handles '--> ... <!--' or '<!-- . . <!-- .. -->' in relation to single line when nothing selected
    # -recognizes incorrect tags like <!----
    # -script can remove also stream comments '/* */' and others own defined, which macro in N++ was not supported
    # -all of this under one shortcut. author: s1w_
    #note: this is NOT begin-end validity checker, use HTML parsers instead
    import re, string
    class commentPair:
        """Objects of this class will contain pairs of comment tags;
        you can define your additional bracket pairs below.
        Constructor takes two parameters: <opening> and <closing>
        raw string describing new definition of comment pair.
        If you want use '\' char in parameter, you have to double it,
        because it is recognized as escape sign. '\\**' and '***\\\\'
        in fact is passed as '\**' and '***\\'. If you forget about
        this, you will have syntax error (quantity of '\' should be
        no different than even). Do not escape other characters!
        Order of defining comment pairs is crucial in this script,
        because it will determine comments recognition order.
        First (top) defined pair is used for main script functionality.
        Additional definitions apply only for operation of cleaning.
        It performs if script detects in selected text or in current
        line one of defined comments: step by step and in the same
        order as were defined (from top to bottom).
        To promote additional comment pair to main functionality, you
        will want to create multiple copies of this script under
        different names and binds each one under different shortcut
        key. Each instance of script may define different order of
        comment pairs."""
        global esc; esc = re.escape
        def __init__( self, left='<!--', right='-->' ):
            self.right = right.strip();  self.left  =  left.strip()
            self.rplus = esc(self.right)
            self.rplus = re.sub(r'^([\\]?.)', r'\1+', self.rplus, 1);
            self.lplus = esc(self.left)+'+'
            self.lmore = self.lplus + '[ \t]*'
            self.rmore = '[ \t]*' + self.rplus
            self.combo = esc(self.left)+'+'+esc(self.right)
            self.tail  = self.right[0]
    #top-most main comment pair definition:
    commentTags  = [ commentPair('<!--', '-->') ]   # always first served and used while commenting line or selection
    #additional comment pairs definitions (only for cleaning):
    commentTags.append( commentPair('/*', '*/') )   # you can add more statements like this in desired order
    #options:
    GREEDY_MODE = 0       #greedy(1): widest matches; non-greedy(0): narrowest matches (recommended)
    MULTIPLE_CLEANING = 1 #allow cleaning multiple braces from selection
    ALIGN_TO_INDENT = 1   #align line to indent if new leading spaces detected (for non-selections)
    CONSOLE_DEBUG = 0     #print script actions to console;
    #declarations:
    position, line = editor.getCurrentPos(), editor.getCurLine()
    currentLine  = editor.lineFromPosition(position)
    indent = editor.getLineIndentPosition(currentLine)
    indstr = re.match('[ \t]*', line).group(0)
    selStart, selEnd = editor.getSelectionStart(), editor.getSelectionEnd()
    cright, cleft = ' '+commentTags[0].right, commentTags[0].left+' '
    alt = position - indent
    #functions:
    def combine_pattern(comm):
        "checks mode and selection dependences and creates adequate pattern compilation for regex functions"
        if selStart == selEnd:
          if not GREEDY_MODE:
                return re.compile('^[ \t]*(.*?)(?P<left>((?P<combo>'+comm.combo+')|(?P<lt>'+comm.lmore+')|(?P<rt>'+comm.rmore+')))(?P<block>.*?)(?P<right>((?(combo)|(?(rt)|(?(lt)((?='+esc(comm.left)+')|'+comm.rmore+'))))|(\r?\n|$)))')
          else: return re.compile('^[ \t]*(?P<b1>(.*?('+comm.combo+'|(?=('+esc(comm.left)+'|'+comm.rplus+'))))+)(?P<left>((?P<cb1>'+comm.combo+')|'+comm.lmore+'|'+comm.rplus+'))(?P<b>.*)(?P<right>((?P<cb2>'+comm.combo+')|'+comm.lmore+'|[ \t]'+comm.rplus+'|((?<![ \t])(?<!<!)(?<!'+esc(comm.tail)+')'+comm.rplus+')))(?P<b2>.*?)(\r?\n|$)'), \
                       re.compile('^.*?(?P<left>((?P<combo>'+comm.combo+')|'+comm.lmore+'|'+comm.rmore+'))')
        else:   return re.compile('('+comm.combo+')|('+esc(comm.left)+')('+esc(comm.tail)+'*[ ]?)|([ ]?'+esc(comm.tail)+'*)('+esc(comm.right)+')'), \
                       re.compile('('+comm.combo+'|'+comm.lplus+'[ ]?|[ ]?'+comm.rplus+')')
    def format_line(comment_pairs):
        global position
        global line; spaces = 0
        if not GREEDY_MODE:
          normal =  combine_pattern(comment_pairs)
          mod = normal.match(line);  mod_exclude = ''
          if CONSOLE_DEBUG:
            console.write('\n\n\n>removing  '+(mod.group("combo") or IfIn(mod.group("left"), comment_pairs.left, comment_pairs.right))+
            '  '+IfIn(mod.group("right"), comment_pairs.right, comment_pairs.left)+'\nfrom line  "'+line.strip()+'"')
          line = normal.sub(r'\1\g<block>', line).rstrip()
        else:
          greed, single = combine_pattern(comment_pairs)
          mod = greed.match(line)
          if CONSOLE_DEBUG: #console output section for debug:
            debug = greed.sub(r'\g<b1>[R1]\g<left>\g<b>\g<right>[R2]\g<b2>', line)
            if mod: console.write('\n\n\nprocessing: '+debug.strip()+'\n'+('='*25))
            chunks = greed.subn(r'\nleft garbage: \g<b1>\nleft tag: \g<left>\nright garbage: \g<b2>\nright tag: \g<right>\n'+('='*25), line)
            if chunks[1]: console.write(chunks[0]+'\n')
          if not mod: #remove single comment without its pair
            mod = single.match(line); mod_exclude = 'right'
            if CONSOLE_DEBUG:
              console.write('\n\n\n>removing single  '+(mod.group("combo") or
              IfIn(mod.group("left"), comment_pairs.left, comment_pairs.right))+'\nfrom line  "'+line.strip()+'"')
            line = re.sub('('+comment_pairs.combo+'|'+comment_pairs.lmore+'|'+comment_pairs.rmore+')', '', line).rstrip()
          else: #determining to remove two comment tags with greedy mode
            flag = 0; mod_exclude = ''; rcombo = False #map of flags was included at the end of script if eventually needed to fathom details
            rcombo = (comment_pairs.left in mod.group("right") and comment_pairs.right in mod.group("right")) #<!---->
            flag = flag|(bool(mod.group("b1").strip())*0x8) #left garbage
            flag = flag|((comment_pairs.right in mod.group("left"))*0x4)  #left tag
            flag = flag|((comment_pairs.right in mod.group("right"))*0x2) #right tag
            flag = flag|(bool(mod.group("b2").strip())*0x1) #right garbage
            if CONSOLE_DEBUG: console.write('flag = '+str("%X" % flag)+', removing: ')
            if flag not in [0x4,0x6,0x8,0xC,0xD,0xE,0xF] and (not rcombo or rcombo and flag not in [0x2,0x3,0xA,0xB]):
              line = re.sub('('+esc(mod.group("b1"))+')'+esc(mod.group("left")), '\\1', line, 1).rstrip() #remove left
              if flag not in [0x2,0x3,0xA,0xB] or rcombo: mod_exclude = 'right'
              if CONSOLE_DEBUG: console.write((mod.group("cb1") or IfIn(mod.group("left"), comment_pairs.left, comment_pairs.right))+'  ')
            if flag not in [0x0,0x1,0x5,0x7,0x9] and (not rcombo or rcombo and flag not in [0x3]):
              line = re.sub('^(.*)'+esc(mod.group("right")), '\\1', line).rstrip() #remove right
              if flag not in [0x2,0x3,0xA,0xB] or rcombo: mod_exclude = 'left'
              if CONSOLE_DEBUG: console.write(mod.group("cb2") or IfIn(mod.group("right"), comment_pairs.right, comment_pairs.left))
        if ALIGN_TO_INDENT:
          spaces = len(re.match('[ \t]*', line).group(0));
          line = line.lstrip();
        if not GREEDY_MODE or ALIGN_TO_INDENT:
          line = indstr+line
        if line.isspace(): line = line.strip()
        checkCursorPosition(mod, spaces, mod_exclude)
        return
    def setSelectionArea(begin, end):
        "checks if selection was in rectangle mode, then sets new selection area"
        if editor.selectionIsRectangle():
          editor.setSelectionMode(1)
          editor.setRectangularSelectionAnchor(begin)
          editor.setRectangularSelectionCaret(end)
        else:
          editor.setSel(begin, end)
    def checkCursorPosition(mod, sp, exclude='', tag='right'):
        "sets new cursor position after line modifications"
        global position;
        newlength = len(line) - len(indstr)
        if alt > len(line.strip()) or newlength < 0:
          position = indent + newlength
          return
        elif not exclude == tag and mod.group(tag):
          spaces = (tag == 'left') and sp or 0
          beg = mod.start(tag) - len(indstr)
          end = mod.end(tag) + spaces - len(indstr)
          if alt > beg:
            if beg < alt and end > alt: position = beg + indent
            else: position -= mod.end(tag)+spaces - mod.start(tag)
        if tag != 'left': checkCursorPosition(mod, sp, exclude, 'left')
        return
    def IfIn(targetStr, *checkargs):
        "function checks targetStr if it contains any of checkargs strings. Returns first match or empty string"
        if targetStr.strip():
          for str in checkargs:
            if str in targetStr: return str
        return ''
    formatted = 0
    #final script code:
    if selStart == selEnd:
        #fast single line modification
        for comments in commentTags:
           if comments.left in line or comments.right in line:
              format_line(comments); formatted = 1; break
        if not formatted:
           if line.isspace(): line = line[:alt-2]
           if alt > len(line.strip()): position = indent + len(line.strip())
           line = re.sub('([ \t]*)(.*)[ \t]*(\r?\n|$)', r'\1'+cleft+r'\2', line).rstrip() + cright
           if indent <= position: position += len(cleft)
           if CONSOLE_DEBUG: console.write('\n\n\n>commenting line...  '+cleft+cright)
        editor.replaceLine(currentLine, line)
        editor.setSel(position, position)
    else:
        #selection modification: if tags detected clear, else comment
        if MULTIPLE_CLEANING:
          for comments in commentTags:
            if formatted: break
            counter = 0; modrange = 0;
            list, rem = combine_pattern(comments)
            for linePos in range(editor.lineFromPosition(selStart), editor.lineFromPosition(selEnd)+1):
              linebeg, lineend = editor.getLineSelStartPosition(linePos), editor.getLineSelEndPosition(linePos)
              lineStr = editor.getTextRange(linebeg, lineend)
              editor.setTargetStart(linebeg); editor.setTargetEnd(lineend)
              if comments.left in lineStr or comments.right in lineStr:
                if CONSOLE_DEBUG: console.write('\n\n>removing  ')
                for tag in list.findall(lineStr):
                  if CONSOLE_DEBUG: console.write((tag[0] or tag[1] or tag[4])+'  '); counter+=1
                  modrange+=len("".join(tag));
                if CONSOLE_DEBUG: console.write('\nfrom selection  "'+lineStr.strip()+'"')
                lineStr = rem.sub('', lineStr)
                editor.replaceTarget(lineStr); formatted = 1
                setSelectionArea(selStart, selEnd-modrange)
            if formatted and CONSOLE_DEBUG:
              console.write('\n:total removals '+str(counter)+'  :total range '+str(modrange))
        if not formatted:
          editor.setTargetStart(selEnd); editor.setTargetEnd(selEnd)
          editor.replaceTarget(cright)
          editor.setTargetStart(selStart); editor.setTargetEnd(selStart)
          editor.replaceTarget(cleft)
          if CONSOLE_DEBUG:
            console.write('\n\n>commenting selection...  '+cleft+' '+cright)
          setSelectionArea(selStart, selEnd+len(cleft)+len(cright))
    #author: s1w_
    #flag_legend_for_greedy_engine: ------------------------------------
    # string                action      flag     condition
    # |-->      *      -->| rem right   0110 x6
    # |abc -->  *      -->| rem right   1110 xE
    # |abc -->  *  --> abc| rem right   1111 xF
    # |-->      *     <!--| rem right   0100 x4
    # |abc -->  *     <!--| rem right   1100 xC
    # |abc -->  * <!-- abc| rem right   1101 xD
    # |abc <!-- *     <!--| rem right   1000 x8
    # |<!--     *      -->| rem right   0010 x2  if right--> === <!---->
    # |abc <!-- *      -->| rem right   1010 xA  if right--> === <!---->
    # |abc <!-- *  --> abc| rem right   1011 xB  if right--> === <!---->
    # |<!--     *  --> abc| rem left    0011 x3  if right--> === <!---->
    # |-->      *  --> abc| rem left    0111 x7
    # |-->      * <!-- abc| rem left    0101 x5
    # |<!--     *     <!--| rem left    0000 x0
    # |<!--     * <!-- abc| rem left    0001 x1
    # |abc <!-- * <!-- abc| rem left    1001 x9
    # |<!--     *      -->| rem both    0010 x2  if right--> != <!---->
    # |<!--     *  --> abc| rem both    0011 x3  if right--> != <!---->
    # |abc <!-- *      -->| rem both    1010 xA  if right--> != <!---->
    # |abc <!-- *  --> abc| rem both    1011 xB  if right--> != <!---->
    # where <!----> === -->
    #testing_samples: --------------------------------------------------
    #    asd <!--asdg ----->  <!----> dd <!--- ---> sdfaf  --->
    #    asd <!---- /**<!-- asdg <!--- */dd --->sdfaf ---> ---> asda
    #    asd <!----> a<!---->sdg<!---->sd ----->  <!----> dd
    #     <!---->        <!--- asdg ----->  <!----> dd <!--- --->