Community
    • Login

    PythonScript ops on selection if any, all text otherwise

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    pythonpythonscriptregex
    18 Posts 3 Posters 1.3k 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.
    • M Andre Z EckenrodeM
      M Andre Z Eckenrode
      last edited by

      I use PythonScript quite a bit with NPP these days, mostly for frequent regular expressions replacements, but I’m hardly fluent in Python, so generally I search the forums here for examples of scripting similar to what I hope to achieve, then try to adapt it for my own needs. I’ve been hoping for some time to modify my regex scripts to make it so they only operate on selected text IF there is any, and on all text otherwise. I just found this post from 2017, which demonstrates how to operate only on selected text, so that’s a big help for me:

      @scott-sumner said in Python script to replace on selection:

      here’s some code that shows how to do a replace on only the text in one or more selections:

      find = 'a'
      replace = 'A'
      num_selections = editor.getSelections()
      for sel_nbr in range(num_selections):
          start_pos = editor.getSelectionNStart(sel_nbr)
          end_pos = editor.getSelectionNEnd(sel_nbr)
          editor.replace(find, replace, 0, start_pos, end_pos)
      

      Is my selected-or-all goal possible, and can anyone give me an example of that if so?

      Michael VincentM 1 Reply Last reply Reply Quote 3
      • Michael VincentM
        Michael Vincent @M Andre Z Eckenrode
        last edited by

        @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

        Is my selected-or-all goal possible, and can anyone give me an example of that if so?

        Yes. Not tested, you could see if the number of selections == 1 (there will always be one) and then test to see if that one has the same start and end position which means there is no selection, just the cursor at that position and then get the range of the entire doc and set start to 0 and end to the returned range.

        if num_selections == 1 and editor.getSelectionNStart(0) == editor.getSelectionNEnd(0):
            start_pos = 0
            end_pos = len(editor.getText()) + 1
            editor.replace(find, replace, 0, start_pos, end_pos)
        else:
           ...
        

        Cheers.

        Alan KilbornA M Andre Z EckenrodeM 3 Replies Last reply Reply Quote 4
        • Alan KilbornA
          Alan Kilborn @Michael Vincent
          last edited by

          @michael-vincent said in PythonScript ops on selection if any, all text otherwise:

          end_pos = len(editor.getText()) + 1

          Works, but I’ll point out that editor.getLength() is available as well as editor.getTextLength(). I think internally they are the same, though, so who would type the longer version?

          Interestingly, in just evaluating my above statement that they are the same, I see that .getLength() “Returns the number of bytes in the document” whereas .getTextLength() “Retrieve the number of characters in the document”. Knowing that for files I work with (UTF-8) the number of bytes and the number of “characters” can definitely be different (bytes >= chars), I tried some tests on files with a lot of multi-byte characters, and the two functions returned the same value. Some further digging showed that it is always return the number of bytes. This is a “good thing” because “position” functions return values that are byte (not char) based, and functions like .rereplace() use position…

          1 Reply Last reply Reply Quote 5
          • Alan KilbornA
            Alan Kilborn @Michael Vincent
            last edited by

            @michael-vincent

            Apologies but I reworked your example a bit:

            (start_pos, end_pos) = (editor.getSelectionNStart(0), editor.getSelectionNEnd(0))
            if num_selections == 1 and start_pos == end_pos:
                start_pos = 0
                end_pos = len(editor.getText()) + 1  # or simpler  editor.getLength()
            editor.replace(find, replace, 0, start_pos, end_pos)
            

            It’s really not changed much, but I think it more fits the model the OP is truly looking for. Of course, your example paves the way for handling multi-selection or column-selection with the nebulous else: ... part, but that’s probably more of a complication than is needed for what the OP described.

            Michael VincentM 1 Reply Last reply Reply Quote 2
            • Michael VincentM
              Michael Vincent @Alan Kilborn
              last edited by PeterJones

              @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

              Apologies but I reworked your example a bit:

              None needed - thank you! I whipped it up quick and knew there was a better way to get document length (.getLength()) but couldn’t quickly find it in Scintilla docs.

              Cheers.

              –
              moderator edit: fixed link

              1 Reply Last reply Reply Quote 2
              • M Andre Z EckenrodeM
                M Andre Z Eckenrode @Michael Vincent
                last edited by

                @michael-vincent said in PythonScript ops on selection if any, all text otherwise:

                if num_selections == 1 and editor.getSelectionNStart(0) == editor.getSelectionNEnd(0):
                    start_pos = 0
                    end_pos = len(editor.getText()) + 1
                    editor.replace(find, replace, 0, start_pos, end_pos)
                else:
                   …
                

                Excellent, thanks! I came up with this for a test run, and worked just fine:

                num_selections = editor.getSelections()
                
                if num_selections == 1 and editor.getSelectionNStart(0) == editor.getSelectionNEnd(0):
                    start_pos = 0
                    end_pos = len(editor.getText()) + 1
                    editor.rereplace(r'0x0x0', ur'•וו', 0, start_pos, end_pos)
                else:
                    for sel_nbr in range(num_selections):
                        start_pos = editor.getSelectionNStart(sel_nbr)
                        end_pos = editor.getSelectionNEnd(sel_nbr)
                        editor.rereplace(r'0x0x0', ur'•וו', 0, start_pos, end_pos)
                

                But most of my actual Python Regex scripts have multiple search/replace steps, up to maybe 15 or so, and most of them are at least somewhat lengthy and complicated, so it would be great if I didn’t have to repeat all those steps in both (selected and non-selected text) sections. I’m thinking the thing to do would be to move all the actual Regex code to a subroutine, and call that from both sections. In a basic internet search for Python subroutines, all the examples I’ve looked at so far deal with passing parameters to them, and receiving values from them, which I don’t think I need to worry about here.

                @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                I’ll point out that editor.getLength() is available as well as editor.getTextLength().

                Thanks, though I’m confused. You reference editor.getTextLength(), allegedly in @michael-vincent ’s posted example, but I’m only seeing editor.getText(), which is actually shorter than editor.getLength(). Typo, maybe? Anyway, good to know about the options.

                @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                @michael-vincent

                your example paves the way for handling multi-selection or column-selection with the nebulous else: ... part, but that’s probably more of a complication than is needed for what the OP described.

                Actually, I’d prefer to keep the ability to handle multiple or columnar selections, just in case.

                Alan KilbornA 3 Replies Last reply Reply Quote 1
                • Alan KilbornA
                  Alan Kilborn @M Andre Z Eckenrode
                  last edited by

                  @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

                  I’m confused. You reference editor.getTextLength(), allegedly in @michael-vincent ’s posted example, but I’m only seeing editor.getText(), which is actually shorter than editor.getLength(). Typo, maybe?

                  Michael used len(editor.getText()) which gets a copy of the text and then calculates its length, via Python’s string length determining function.

                  I mentioned that length is available by asking Scintilla directly for just the number, via editor.getLength() or editor.getTextLength() – no retrieving the text (since it is unnecessary to have the text).

                  Different things, no “typo”.

                  M Andre Z EckenrodeM 1 Reply Last reply Reply Quote 2
                  • Alan KilbornA
                    Alan Kilborn @M Andre Z Eckenrode
                    last edited by

                    @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

                    I’d prefer to keep the ability to handle multiple or columnar selections

                    Well, then I’d refer you back to the code you quoted in your FIRST posting in this thread, as that code handles these types of selections.

                    Note that if you have “n” selections (and a column block is really “n” selections, where “n” is the number of rows in the selection), you have to do “n” separate editor.rereplace() calls – there’s no way to do it in one call.

                    1 Reply Last reply Reply Quote 3
                    • M Andre Z EckenrodeM
                      M Andre Z Eckenrode @Alan Kilborn
                      last edited by

                      @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                      I mentioned that length is available by asking Scintilla directly for just the number, via editor.getLength() or editor.getTextLength() — no retrieving the text (since it is unnecessary to have the text).

                      Ok, I see now. I missed that distinction. Thanks for the clarification.

                      1 Reply Last reply Reply Quote 2
                      • Alan KilbornA
                        Alan Kilborn @M Andre Z Eckenrode
                        last edited by

                        @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

                        most of my actual Python Regex scripts have multiple search/replace steps, up to maybe 15 or so, and most of them are at least somewhat lengthy and complicated, so it would be great if I didn’t have to repeat all those steps

                        Maybe a list of tuples meets the need here?

                        my_find_repl_tup_list = [
                            ( r'find_str_1', r'repl_str_1'),
                            ( r'find_str_2', r'repl_str_2'),
                            ( r'find_str_3', r'repl_str_3'),
                            ]
                        

                        Then you could loop over the list:

                        for (find, replace) in my_find_repl_tup_list:
                            ...
                        
                        M Andre Z EckenrodeM 1 Reply Last reply Reply Quote 3
                        • M Andre Z EckenrodeM
                          M Andre Z Eckenrode @Alan Kilborn
                          last edited by

                          @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                          Maybe a list of tuples meets the need here?

                          Well, it LOOKS promising, but I evidently can’t figure out how to utilize it. Based on your example as shown in your post, I adapted the find/replace code from my own previous post, consulted this page and tried to use it here, but…

                          thistuple = [
                              ( r'0x0x0', ur'•וו'),
                          

                          …etc. gave me an invalid syntax error with their print demo example. I then tried…

                              ( "r'0x0x0', ur'•וו'"),
                          

                          …and that seemed to work fine with the print demo, so I tried plugging that into my test code above, as such:

                          findrepltuple = [
                              ( "r'0x0x0', ur'•וו'"),
                              ( "r'this', ur'that'"),
                              ]
                          
                          num_selections = editor.getSelections()
                          
                          if num_selections == 1 and editor.getSelectionNStart(0) == editor.getSelectionNEnd(0):
                              start_pos = 0
                              end_pos = len(editor.getText()) + 1
                              for (find, replace) in findrepltuple:
                                  editor.rereplace(findrepltuple, 0, start_pos, end_pos)
                          else:
                              for sel_nbr in range(num_selections):
                                  start_pos = editor.getSelectionNStart(sel_nbr)
                                  end_pos = editor.getSelectionNEnd(sel_nbr)
                                  for (find, replace) in findrepltuple:
                                      editor.rereplace(findrepltuple, 0, start_pos, end_pos)
                          

                          Also tried editor.rereplace((find, replace), 0, start_pos, end_pos).

                          Result for both was error “for (find, replace) in findrepltuple: ValueError: too many values to unpack” from PythonScript.

                          Alan KilbornA 2 Replies Last reply Reply Quote 0
                          • Alan KilbornA
                            Alan Kilborn @M Andre Z Eckenrode
                            last edited by

                            @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:
                            "r'0x0x0', ur'•וו'"
                            "r'this', ur'that'"

                            Hmm, outer quotes are not right (delete them).

                            I used r prefix on the sample strings in my demo example because typically regular-expressions contain a lot of \ and by using the r prefix the backslashes don’t have to be doubled, leading to easier-to-read strings. If your regexes don’t use backslashes like shown here, you don’t have to use the r prefix.

                            So maybe try:

                            findrepltuple = [
                                ( '0x0x0', u'•וו'),
                                ( 'this', u'that'),
                                ]
                            
                            1 Reply Last reply Reply Quote 1
                            • Alan KilbornA
                              Alan Kilborn @M Andre Z Eckenrode
                              last edited by

                              @m-andre-z-eckenrode

                              Also, this won’t work:

                              editor.rereplace(findrepltuple, 0, start_pos, end_pos)

                              This function will require a separate find and replace expression.

                              So:

                              editor.rereplace(find, replace, 0, start_pos, end_pos)

                              M Andre Z EckenrodeM 1 Reply Last reply Reply Quote 2
                              • M Andre Z EckenrodeM
                                M Andre Z Eckenrode @Alan Kilborn
                                last edited by

                                @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                                Hmm, outer quotes are not right (delete them).

                                Well, they made the online print demo work, anyway.

                                If your regexes don’t use backslashes like shown here, you don’t have to use the r prefix.

                                Oh, they’re all over the place in my regexes. My standard operating procedure in PythonScript is `editor.rereplace(r’find string’, ur’replace string’).

                                @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                                Also, this won’t work:

                                editor.rereplace(findrepltuple, 0, start_pos, end_pos)

                                Got it, and now working! Thanks much! Though I do have one gripe, which I can live with, about doing it this was: In my multi-step Regex scripts, because most steps ARE fairly long and complicated and I sometimes need to revise them, I generally precede each one with a comment giving examples of before and after text so it’s easier to zero in on when necessary, but I clearly (that I know of) can’t do that when using a tuple to store all the find/replace expressions. The next best solution that I can think of is to just have all the comments compiled together sequentially, corresponding to the order of the expression pairs, and comments such as # Regex 1 after each pair. Unless anybody has a better suggestion.

                                Alan KilbornA 2 Replies Last reply Reply Quote 0
                                • Alan KilbornA
                                  Alan Kilborn @M Andre Z Eckenrode
                                  last edited by Alan Kilborn

                                  @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

                                  Unless anybody has a better suggestion.

                                  findrepltuple = []
                                  
                                  #-----------------------------------------------------
                                  
                                  findrepltuple.append(('0x0x0', u'•וו'))
                                  
                                  '''
                                  as much text as you want about the above 
                                  blah blah blah...
                                  blah blah blah...
                                  blah blah blah...
                                  blah blah blah...
                                  '''
                                  
                                  #-----------------------------------------------------
                                  
                                  findrepltuple.append(('this', u'that'))
                                  
                                  '''
                                  as much text as you want about the above 
                                  blah blah blah...
                                  blah blah blah...
                                  blah blah blah...
                                  blah blah blah...
                                  '''
                                  
                                  #-----------------------------------------------------
                                  
                                  etc.
                                  
                                  Alan KilbornA 1 Reply Last reply Reply Quote 3
                                  • Alan KilbornA
                                    Alan Kilborn @M Andre Z Eckenrode
                                    last edited by

                                    @m-andre-z-eckenrode said in PythonScript ops on selection if any, all text otherwise:

                                    I generally precede each one

                                    Sorry, I didn’t catch that part so the sample I provided shows the explanatory text AFTER the live-code part, not before. :-(
                                    Pretty easy to see how to modify it, though. :-)

                                    M Andre Z EckenrodeM 1 Reply Last reply Reply Quote 1
                                    • M Andre Z EckenrodeM
                                      M Andre Z Eckenrode @Alan Kilborn
                                      last edited by

                                      @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                                      findrepltuple = []
                                      
                                      #-----------------------------------------------------
                                      
                                      findrepltuple.append(('0x0x0', u'•וו'))
                                      
                                      '''
                                      as much text as you want about the above 
                                      blah blah blah...
                                      '''
                                      

                                      Cool, that works. Thanks again.

                                      @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                                      Sorry, I didn’t catch that part so the sample I provided shows the explanatory text AFTER the live-code part, not before.

                                      No problem, either way is good.

                                      .

                                      1 Reply Last reply Reply Quote 1
                                      • Alan KilbornA
                                        Alan Kilborn @Alan Kilborn
                                        last edited by

                                        @alan-kilborn said in PythonScript ops on selection if any, all text otherwise:

                                        .append((‘0x0x0’, u’•וו'))

                                        Note that append is a function call to add something to a list. In this case we are adding a tuple to the list, so that’s why the opening and closing parentheses are doubled – the outer pair is for the function call, the inner pair is the tuple notation.

                                        1 Reply Last reply Reply Quote 2
                                        • guy038G guy038 referenced this topic on
                                        • First post
                                          Last post
                                        The Community of users of the Notepad++ text editor.
                                        Powered by NodeBB | Contributors