Community
    • Login

    Copy entire code group?

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    11 Posts 5 Posters 4.9k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • guy038G
      guy038
      last edited by

      Hello @arel3 andAll,

      One easy possibility is to grab all the text with a regular expression !

      For instance, in order to select all the text <table>tons of code</table>

      • Open the Mark dialog

      • SEARCH (?s-i)^\h*<table>.+?</table>

      • Untick all options

      • Tick the Purge for each search and Wrap around options

      • Select the Regular expression search mode

      • Click on the Mark All button

      • Click on the Copy Marked text button

      • Open a new tab and Paste the clipboard contents

      => You should get all text between the <table> and </table> tags, including the tags themselves

      Best Regards,

      guy038

      arel3A 1 Reply Last reply Reply Quote 2
      • arel3A
        arel3 @guy038
        last edited by

        Thank you very much for the reply, @guy038!

        Is there a faster way to do it than the 4-step way I figured out and shared?

        Something like holding a key and clicking on a <tag>?

        PeterJonesP 1 Reply Last reply Reply Quote 0
        • PeterJonesP
          PeterJones @arel3
          last edited by PeterJones

          @arel3 ,

          Is there a faster way to do it than the 4-step way I figured out and shared?

          Guy’s method is faster than what you described. (edit: oh, except that it’s limited to a single hardcoded tag type, whereas your problem statement could be interpreted as “any random tag I click on”)

          Notepad++ has a built-in Search > Select All Between Matching Braces, which will select everything from ( to ) or { to }. But it does not have a built in equivalent Select All Between Matching Tags – which is mildly surprising, since the logic is basically identical.

          Such a feature might already exist in one of the HTML or XML plugins, because there are some of those that add some pretty cool features for people who are regularly editing markup-language files (I am not included in that set, so I don’t know the features and pluses/minuses of those plugins).

          If it’s not there, one could write a script for the PythonScript plugin that did that… I might have a think about that implementation, if I find the time today.

          PeterJonesP 1 Reply Last reply Reply Quote 1
          • PeterJonesP
            PeterJones @PeterJones
            last edited by

            @peterjones said in Copy entire code group?:

            I might have a think about that implementation, if I find the time today.

            It was interesting enough to me that I took a script that had similar logic and expanded it.

            1. Install PythonScript plugin if not already installed (Plugins > Plugins Admin, check Python Script then click Install
            2. Create a new script in PythonScript (Plugins > PythonScript > New script, name = select-current-tag.py)
            3. Paste the contents from below and save
            4. Run Plugins > PythonScript > Scripts > select-current-tag to run the script.
            5. If you want to assign a keyboard shortcut:
              1. Plugins > PythonScript > Configuration…
              2. In User Scripts, click on select-current-tag and then the left Add button (to add it to the Menu Items list on the left)
              3. OK
              4. Exit and Restart Notepad++
              5. Use Settings > Shortcut Mapper, Plugins tab; filter on Python; the Modify the shortcut for select-current-tag

            You only have to do step#4 once: after that, every time you start Notepad++, that script will be available through your shortcut.

            To use it, click on the <tagname or </tagname then run Plugins > PythonScript > Scripts > select-current-tag or the keystroke you chose in #4. This will select from <tagname thru </tagname>, and then you can easily copy that with Ctrl+C. Caveats:

            • If you click after a space (for example, on class in <tagname class="...">), it will not find the tag properly
            • If you click after the > (for example, after > in <tagname>), it will not find the tag properly
            • Currently, the script assumes that you want to find the closing tag immediately after the active open tag, or the opening tag immediately before the active close tag. It does not find balanced tags, because that is hard. Thus, if you have nested divs, it might not do what you desire. But Guy made the same assumption, and most HTML tags don’t nest anyway, so except for div and a few others, it’s probably not a big deal. If it is a big deal to your use case, then let us know, and maybe someone (maybe even me) will be interested enough to modify my script to require balanced tags.
            • If you have lots of confusing things, like > inside attributes or using <!-- </div> --> to comment out tags that you want to skip, this won’t handle it. I am not going to write a whole HTML/XML parser just to satisfy a complicated sequence like that. If you need something that complicated, a full-blown HTML or XML plugin will be required, and you will need to investigate whether any of the existing plugins already have the select-tag-and-contents feature for you.
            # encoding=utf-8
            """ selectCurrentTag: in response to https://community.notepad-plus-plus.org/topic/22965/copy-entire-code-group
            
            Starts at the current position; looks to see if it's `<open`, `</close`, or neither.
            If not neither, then search for the first pair it finds.
            
            TODO: change from  "first pair it finds" to "first balanced pair it finds"
            """
            
            from Npp import editor, notepad, console
            #console.show()
            
            class SelectCurrentTag(object):
                class TAGTYPE(object):
                    OPEN = 'OPEN'
                    CLOSE = 'CLOSE'
                    OTHER = None
            
                #def __init__(self):
                #    self.oldPos = editor.getCurrentPos()
                #    console.write("SelectCurrentTag: initialized\n")
            
                def getTagWord(self):
                    self.oldPos = editor.getCurrentPos()
                    self.tagword = editor.getCurrentWord()
            
                    self.tagtype = self.TAGTYPE.OTHER
            
                    self.wordtagpos = [None,None]
                    direction = 0
                    searchpos = self.oldPos
                    #console.write(__file__ + "::" + __name__ + "::{}..{}".format(searchpos, self.oldPos) + "\n")
                    while searchpos > 0 and searchpos < editor.getLength():
                        c = editor.getCharAt(searchpos)
                        if c<0:
                            # utf8: first byte&0xC0 is 0xC0; subsequent bytes in the char are 0x80
                            # thus, step backword a byte while those aren't start-byte
                            while searchpos>0 and c & 0xc0 != 0xc0:
                                searchpos -= 1
                                c = editor.getCharAt(searchpos)
                                q = editor.positionAfter(searchpos)
                                #console.write("\t{}: {}..{}\n".format("searching", searchpos, q))
            
                            #console.write("\t{}: {}..{}\n".format("found", searchpos, q))
                            s = editor.getTextRange(searchpos,q).decode('utf-8')
                            if len(s)==1:
                                c = ord(s)
                            elif len(s)==2:
                                c = 0x10000 + (ord(s[0]) - 0xD800) * 0x400 + (ord(s[1]) - 0xDC00)
                            else:
                                c = ord(s)  # will probably give an exception
            
                        elif c>255:
                            console.writeError("unknown character {} while searching for \\".format(c))
                            s = unichr(c)   # should probably create an exception
            
                        else:
                            s = unichr(c)
            
                        #info = "#{0:5}# '{2}' = HEX:0x{1:04X} = DEC:{1} ".format(searchpos, c, s.encode('utf-8') if c not in [13, 10, 0] else 'LINE-ENDING' if c != 0 else 'END-OF-FILE')
                        #console.write(info + "\n")
            
                        if c in [0, 10, 13, 8, 32] and direction==0: # nul, newline, horizontal whitespace not allowed when searching for start of the current open/close tag
                            self.wordtagpos[direction] = None
                            self.tagtype = self.TAGTYPE.OTHER
                            break
            
                        if s == '<' and direction==0:
                            self.wordtagpos[direction] = searchpos
                            if editor.getTextRange(searchpos+1,searchpos+2) == '/':
                                self.tagtype = self.TAGTYPE.CLOSE
                            else:
                                self.tagtype = self.TAGTYPE.OPEN
            
                            # since I found it, start from original and change direction
                            searchpos = self.oldPos - 1
                            direction = 1 # was: break
            
                        if s == '>' and direction==1:
                            self.wordtagpos[direction] = searchpos+1  # include the '>'
                            if self.tagword == '':
            
                                def assn(m):
                                    self.tagword = m.group(1)
                                    #console.write("assn called: 0:'{}' 1:'{}'\n".format(m.group(0), m.group(1)))
            
                                editor.research(r'</?(\w+).*?>', lambda m: assn(m), 0, self.wordtagpos[0], self.wordtagpos[1], 1)
                                pass
                            break
            
                        # if not found yet, go back a character and loop
                        searchpos += (2*direction-1)    # so direction==0 will subtract 1, direction==1 will add 1
            
                    #console.write("SelectCurrentTag: {:d}..{:d}..\n".format(foundstartpos, self.oldPos))
            
                    return self.tagword, self.tagtype
            
                def selectFromThisOpen(self):
                    """selects from the beginning of the current-position-open tag to the first close tag found"""
                    # TODO: require balanced tags inside
                    self.startNewSelection = self.wordtagpos[0]
            
                    def cb(m):
                        #console.write("->selectFromThisOpen: 0:'{}' at ({}..{})\n".format(m.group(0), m.start(), m.end()))
                        self.endNewSelection = m.end()
            
                    editor.research(r'</{}.*?>'.format(self.tagword), cb, 0, self.wordtagpos[0], editor.getLength(), 1) # find the first
            
                    #console.write("->selectFromThisOpen => {} .. {}\n".format(self.startNewSelection, self.endNewSelection))
                    editor.setSelection(self.startNewSelection, self.endNewSelection)
            
                def selectToThisClose(self):
                    """selects from the last open-tag found to the close tag known at the current location"""
                    self.endNewSelection = self.wordtagpos[1]
            
                    self.startNewSelection = 0  # TODO: actually find the previous open tag (or eventually, matching open tag)
                    def cb(m):
                        #console.write("->selectToThisClose: 0:'{}' at ({}..{})\n".format(m.group(0), m.start(), m.end()))
                        self.startNewSelection = m.start()
            
                    editor.research(r'<{}.*?>'.format(self.tagword), cb, 0, 0, self.wordtagpos[1]) # find the last, so don't put a maxCount parameter
            
                    #console.write("->selectToThisClose => {} .. {}\n".format(self.startNewSelection, self.endNewSelection))
                    editor.setSelection(self.startNewSelection, self.endNewSelection)
            
                def go(self):
                    self.getTagWord()
                    #console.write("SelectCurrentTag: tagword='{:s}' tagtype={:s} oldPos={:d} wordtagpos={}\n".format(self.tagword, self.tagtype, self.oldPos, self.wordtagpos))
                    if self.tagtype == self.TAGTYPE.OPEN:
                        self.selectFromThisOpen()
                        pass
                    elif self.tagtype == self.TAGTYPE.CLOSE:
                        self.selectToThisClose()
                        pass
                    else:   # no matching tag found, so don't select anything new
                        editor.setSelection( self.oldPos, self.oldPos )
                        pass
            
            SelectCurrentTag().go()
            
            1 Reply Last reply Reply Quote 0
            • PeterJonesP PeterJones referenced this topic on
            • Alan KilbornA
              Alan Kilborn
              last edited by

              @arel3 said in Copy entire code group?:

              An example of what I mean is the + and - that allows collapsing a group of code…

              Maybe I’m missing something, but isn’t what the OP is asking about something in the spirit of the following example?:

              • Open langs.xml
              • Collapse line 4 to get a + before it showing in its margin:
                76f507b8-3d72-45e7-96f4-94f290f93218-image.png
              • Start selecting on line 4, and go downward, making sure your caret is on line 408 when you stop selecting (but it is only the next visible line downward, so really easy to do):
                5e71ac98-5bf2-42cc-af27-bf84a2954377-image.png
              • Press Ctrl+c
              • Move to a new tab
              • Press Ctrl+v to see that 400-some lines were copied/pasted, between the <Languages> tag and the </Languages> tag.
              PeterJonesP 1 Reply Last reply Reply Quote 2
              • PeterJonesP
                PeterJones @Alan Kilborn
                last edited by PeterJones

                @alan-kilborn said in Copy entire code group?:

                Maybe I’m missing something,

                Nope, I think I was missing something. That’s much more obvious, and macro recordable: Fold, Alt+Home, Alt+Home (*), select-line-down, copy, arrow up, unfold.

                        <Macro name="CopyCurrentTagWithContents" Ctrl="yes" Alt="yes" Shift="yes" Key="67">
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 44030 = IDM_VIEW_FOLD_CURRENT" />
                            <Action type="2" message="0" wParam="44030" lParam="0" sParam="" />
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 2345:HomeDisplay x2 to make sure at the first line of the collapsed level" />
                            <Action type="0" message="2345" wParam="0" lParam="0" sParam="" />
                            <Action type="0" message="2345" wParam="0" lParam="0" sParam="" />
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 2301:LineDownExtend: select the whole block" />
                            <Action type="0" message="2301" wParam="0" lParam="0" sParam="" />
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 2178:Copy" />
                            <Action type="0" message="2178" wParam="0" lParam="0" sParam="" />
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 2302:LineUp: back to the start of the block" />
                            <Action type="0" message="2302" wParam="0" lParam="0" sParam="" />
                            <Action type="0" message="2172" wParam="0" lParam="0" sParam="COMMENT: 44031 = IDM_VIEW_UNFOLD_CURRENT" />
                            <Action type="2" message="0" wParam="44031" lParam="0" sParam="" />
                        </Macro>
                

                After recording the macro and assigning it to Ctrl+Alt+Shift+C, and restarting Notepad++, I added in comments to the macro in shortcuts.xml to help it be understandable what’s going on, then I used that macro to grab the macro source out of shortcuts.xml, which made it really easy to prove that it was truly working after restart. ;-)

                So, instead of installing PythonScript and my almost-good-enough script, you could instead just record that macro yourself. Or, if you want, follow the sequence:

                1. Exit all Notepad++ and restart one instance
                2. Open %AppData%\Notepad++\shortcuts.xml (or your portable or cloud or alternate-location equivalent shortcuts.xml)
                3. In the <Macros> section of the shortcuts.xml file, add the macro code from this post
                4. Save, Exit Notepad++, and restart.
                5. From now on, Ctrl+Alt+Shift+C will run that macro to copy the current tag with its contents.
                  • You can use Macro > Modify Shortcut/Delete Macro to change the shortcut if you don’t like mine.

                That command might become part of my usage when copying macro source XML into the forum. ;-) So thanks, @Alan-Kilborn , for the simplified procedure, and to @arel3 for asking the question which prompted it.

                –
                *: Why two Alt+Home while recording the macro (aka, two 2345=SCI_HOMEDISPLAY in the macro source)? Because if your cursor happens to be inside of a folded section of text, the first SCI_HOMEDISPLAY takes you the end of the first (visible) line of the folded section, then the second Alt+Home takes you to the very beginning of the physical line. Alt+Home will work if you have the default keyboard shortcuts; if you’ve modified it like I have, SCI_HOMEDISPLAY might be a different keystroke for you.

                PeterJonesP 1 Reply Last reply Reply Quote 2
                • PeterJonesP
                  PeterJones @PeterJones
                  last edited by

                  @Alan-Kilborn , @arel3 , et alia,

                  The other benefit of the macro sequence, compared to Search > Select All Between Matching Braces or my earlier script, is that it will work for any foldable chunk of text, whether it’s the { ... } code block in C/C++ and Perl, or the indented code block in Python, or the <tag>...</endtag> in HTML/XML. It will even work in the simple UDL code-folding, if you have properly defined that in your UDL.

                  So that’s graduated to being something that I think I might be able to incorporate into my workflow.

                  arel3A 1 Reply Last reply Reply Quote 2
                  • arel3A
                    arel3 @PeterJones
                    last edited by

                    Wow, thank you @peterjones!
                    I do mainly use the program for markup languages. I had more expectation that being able to do this was already possible, not that someone would put the time in to write a script for it if it didn’t. Thank you!!!
                    I’m sure others can make use of this too since you’ve made it capable of working with any foldable text.

                    Yes, that’s what I meant; foldable text seems to be the term I didn’t know.

                    rdipardoR 1 Reply Last reply Reply Quote 1
                    • rdipardoR
                      rdipardo @arel3
                      last edited by

                      @arel3,
                      Have you heard about the HTML Tag plugin?

                      npp_841_x64_htmltag_v123.gif

                      arel3A 1 Reply Last reply Reply Quote 2
                      • arel3A
                        arel3 @rdipardo
                        last edited by

                        @rdipardo said in Copy entire code group?:

                        @arel3,
                        Have you heard about the HTML Tag plugin?

                        npp_841_x64_htmltag_v123.gif

                        This is the solution, thank you!

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post
                        The Community of users of the Notepad++ text editor.
                        Powered by NodeBB | Contributors