Automatic selection of ALL instances of a searched string, in one go
-
Hello, All,
Here is a simple
Python
script with automatically select all instances of a searched zone ! It comes from this initial @scott-sumner script, at the location :https://github.com/notepad-plus-plus/notepad-plus-plus/issues/8203#issuecomment-624124396
# -*- coding: utf-8 -*- # Refer to : https://github.com/notepad-plus-plus/notepad-plus-plus/issues/8203#issuecomment-624124396 ( from @sasumner ) import re from Npp import editor, notepad sel_start = editor.getSelectionStart() sel_end = editor.getSelectionEnd() selected = sel_end - sel_start user_search = notepad.prompt('Use ?(i) if INSENSITIVE search and / or SURROUND with \\b for a WHOLE WORD search', 'Enter the REGEX to search for, on existing STREAM selection :', '(?i)') if user_search != None and len(user_search) > 0 and selected > 50: # For TINY selections this script is USELESS ! span_match_list = [] editor.research(user_search, lambda m: span_match_list.append(m.span(0)), 0, sel_start, sel_end) if len(span_match_list) > 0: if not editor.getMultipleSelection(): editor.setMultipleSelection(True) # If NOT enabled in 'Preferences... > Editing' first = True print len(span_match_list) for m in span_match_list: if first: editor.setSelection(m[1], m[0]) first = False else: editor.addSelection(m[1], m[0])
To use that script, follow these steps :
-
Click or open the document to search for
-
Do any stream selection, of more than fifty chars, within the current document
-
Run the above
Python
script -
Choose the
regex
string to search for -
Click on the
OK
button and enjoy !
For instance, let’s use this EXAMPLE text below :
This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test
-
Select the
10
middle lines, which are shifted by two postions -
Run the above script
-
Type in the string
simple
-
Click on the
OK
button
=> At once, the ten words
simple
of the initial selection are now selected individually. This means that you may delete them, add some text before or after or even hit theEnter
key !Note that I used, on purpose, shifted lines to prevent using any
rectangular
selection !
As you see, that’s quite easy :
-
Choose a document
-
Make a pre-selection
-
Run the script
Moreover, as the script asks you for a
regex
string, you can, practically, search for nearly anything ! Of course, simple strings are also allowed with the possible additionnal zones(?i)
and/or\b
. For instance :-
You may search for the string
the
, whatever its case , typing in the string(?i)the
-
You may search for the whole word
the
, with this case, typing in\bthe\b
But these examples are also correct :
-
You may search for smallest ranges of text between two lowercase letters
t
, typing in the stringt.*?t
-
You may search for any multi-lignes zones, beginning with
<td
and ending with</td>
, typing in the string(?s)<td.+?</td>
Let’s go back to the initial example :
This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test
Of course, if we want to replace the text
simple test
withvery simple text to do
in the shifted lines only, just use the Replace dialog and click on theIn selection
optionNow, we can also use the
Python
script :-
Do a normal selection of the
10
shifted lines -
Run the
Python
script -
Enter the string
simple
-
Click on the
OK
button -
Then, hit the
Left arrow
key -
Type in the string
very
, followed with aspace
char -
Hit the
End
key -
Type in a
space
char followed with the stringto do
A last test :
-
As before, select the
10
shifted lines -
Open the Mark dialog
-
Type in the string
simple
in the Find what : zone -
Uncheck all box options
-
Check the
In selection
box -
Click on the
Mark All
button
=> Ten
red
marked zones appear in the selection-
Close the Mark dialog with the
ESC
key -
Run the
Python
script -
Type in the string
simple
-
Click on the
OK
button
=> As you can see, the marked regions and the marked selections of the script are strictly identical
So, I wonder if we should propose an enhancement of the
Mark
dialog, with a new box optionSelection of the Occurrences
or equivalent, using this mechanism ?Best Regards,
guy038
P.S. : This modified version is just a first draft of the @Scott-sumner script and it could certainly be enhanced !
-
-
Here’s a slightly tidier vesion of that script.
Major changes:- searches the whole document when the user has no selection.
- Compatible with Python 3 (original was not due to print statement)
- Notifies user when selection is too small
# -*- coding: utf-8 -*- # Refer to : https://github.com/notepad-plus-plus/notepad-plus-plus/issues/8203#issuecomment-624124396 ( from @sasumner ) from __future__ import print_function # for Python2 compatibility from Npp import editor, notepad sel_start = editor.getSelectionStart() sel_end = editor.getSelectionEnd() selected = sel_end - sel_start if not selected: # no user selection, get whole document sel_start = 0 sel_end = editor.getLength() selected = sel_end user_search = notepad.prompt( 'Use (?i) if INSENSITIVE search and / or SURROUND with \\b for a WHOLE WORD search', 'Enter the REGEX to search for, on existing STREAM selection :', '(?i)') print('Search: %s, selected range: (%d, %d)' % (user_search, sel_start, sel_end)) if user_search: if selected < 50: notepad.messageBox('This script is useless for selections of less than 50 characters', 'aborting select_all_regex_matches due to small selection') else: span_match_list = [] editor.research(user_search, lambda m: span_match_list.append(m.span(0)), 0, sel_start, sel_end) if span_match_list: if not editor.getMultipleSelection(): # If NOT enabled in 'Preferences... > Editing' editor.setMultipleSelection(True) first = True print('Got %d matches' % len(span_match_list)) for m in span_match_list: editor.addSelection(m[1], m[0])
-
Hello, @mark-olson and All,
Many thanks, Mark, for your improved version !
But, as we’re leaving for four days to camp and do some hiking, with some friends, in the south-east of France ( Drome department ), I won’t be testing your version until Tuesday evening at the earliest !
Have a nice weekend !
BR
guy038
-
@Mark-Olson said in Automatic selection of ALL instances of a searched string, in one go:
selected = sel_end - sel_start
The scintilla documentation notes that the first selection should be done with
SCI_SETSELECTION
and the following ones withSCI_ADDSELECTION
. Your example seems to work, but I’m not sure it’s intended that way.
Also, for this particular case where you want to get either the selection from the user or the whole document, PS has a nice helper functioneditor.getUserCharSelection()
that makes the code even tidier :-)sel_start, sel_end = editor.getUserCharSelection() selected = sel_end - sel_start
And if there is no need for regex because one wants to select the current word instances there are SCI_MULTIPLESELECTADDNEXT and SCI_MULTIPLESELECTADDEACH
which makes it even more tidier again :-D
from Npp import editor, FINDOPTION editor.setSearchFlags(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE) editor.targetWholeDocument() editor.multipleSelectAddNext()
from Npp import editor, FINDOPTION first_line = editor.getFirstVisibleLine() editor.setSearchFlags(FINDOPTION.WHOLEWORD | FINDOPTION.MATCHCASE) editor.targetWholeDocument() editor.multipleSelectAddEach() editor.rotateSelection() editor.setFirstVisibleLine(first_line)
-
Hello, @mark-olson, @ekopalypse and All,
So, after merging informations, provided by both @mark-olson and @ekopalypse, I ended up with this new version of the @mark-olson one, which uses the
editor.addSelection
mechanism# -*- coding: utf-8 -*- # An Adaptation of the @Mark-Olson's Python script with the advice of @ekopalypse : # # https://community.notepad-plus-plus.org/topic/24549/automatic-selection-of-all-instances-of-a-searched-string-in-one-go/2 ( and .../4 ) # # # See also : https://github.com/notepad-plus-plus/notepad-plus-plus/issues/8203#issuecomment-624124396 ( from @sasumner ) # # # from __future__ import print_function # for Python2 compatibility # from Npp import editor, notepad sel_start, sel_end = editor.getUserCharSelection() selected = sel_end - sel_start if selected < 50: notepad.messageBox('Selection >= 50 characters or NO selection is MANDATORY', 'IMPORTANT :', 0) else: user_search = notepad.prompt( 'Use (?i) if INSENSITIVE search and / or SURROUND with \\b for a WHOLE WORD search', 'Enter the WORD, STRING or REGEX to search for :', '(?i)') if user_search: span_match_list = [] editor.research(user_search, lambda m: span_match_list.append(m.span(0)), 0, sel_start, sel_end) print() print('%d matches of \'%s\', in range [%d, %d]' % (len(span_match_list), user_search, sel_start, sel_end)) # if not editor.getMultipleSelection(): editor.setMultipleSelection(True) # If NOT enabled in 'Preferences... > Editing' first = True for m in span_match_list: if first: editor.setSelection(m[1], m[0]) first = False else: editor.addSelection(m[1], m[0])
Notes :
-
Always click anywhere within current document to cancel any NON-wanted possible selection(s), BEFORE running this script !
-
Then, select a range of lines or regions to restrict the future matches to that range or do nothing if future matches will concern the whole current document
-
Finally, run the script and enter the word, string or regex to search for
Remarks :
-
Apparently, the import
from __future__ import print_function
, for Python2 compatibility, does not seem necessary !? -
On the same way, the instruction
if not editor.getMultipleSelection(): editor.setMultipleSelection(True)
, If NOT enabled inPreferences... > Editing
, is not needed, too !
Now, regarding the @ekopalypse’s script, which uses the
editor.multipleSelectAddEach
mechanism, I simply modified the lineeditor.multipleSelectAddEach()
As :
if editor.getSelectionEmpty(): editor.multipleSelectAddEach() editor.multipleSelectAddEach()
Because, in case of NO selection, the first instruction
editor.multipleSelectAddEach()
simply selects the current word !Best Regards,
guy038
-
-
-
@guy038 said in Automatic selection of ALL instances of a searched string, in one go:
Apparently, the import from future import print_function, for Python2 compatibility, does not seem necessary !?
It is necessary for anyone running an older version of PythonScript that uses Python 2. In Python 2, you would write
print 'hello world'
but in python3 it’s
print('hello world')
-
@guy038 said in Automatic selection of ALL instances of a searched string, in one go:
Because, in case of NO selection, the first instruction editor.multipleSelectAddEach() simply selects the current word !
The if-check is not necessary because the current word is automatically selected, as you said.
-
@Mark-Olson said in Automatic selection of ALL instances of a searched string, in one go:
older version of PythonScript that uses Python 2
Note that PythonScript plugin version 2 is not necessarily “older”; until the devs of that plugin stop calling version 3 an “alpha” version and retire version 2, version 2 should be considered the mainstream version.
-
IMHO the Automatic selection of ALL instances of a searched string, in one go should be implemented into the Search Replace dialog of Npp
-
@wonkawilly said in Automatic selection of ALL instances of a searched string, in one go:
Automatic selection of ALL instances of a searched string, in one go should be implemented into the Search Replace dialog of Npp
And the reason for this is…?
Please be specific and give tangible advantages for needing to do this.(Guy’s script presented in this thread is one thing, and it is fine to have done, but if we’re debating what should/shouldn’t be in the N++ program natively, then things go a little deeper)
-
@Alan-Kilborn said in Automatic selection of ALL instances of a searched string, in one go:
@wonkawilly said in Automatic selection of ALL instances of a searched string, in one go:
Automatic selection of ALL instances of a searched string, in one go should be implemented into the Search Replace dialog of Npp
And the reason for this is…?
Please be specific and give tangible advantages for needing to do this.Very trivial Scenarios
-
Suppose a user has a file that contains data that needs be to updated. That data is in columns: in this scenario usually it is used column / rectangular selection and, for example, column editor to add a progressive number. Easy and fast.
-
But in the moment that the data that must be updated is no more in column the user can’t use rectangular selection so neither column editor at the moment. The user will need to do it manually or to look for a work around of some kind like selecting by hand each one of the instances with all the consequences in term of time spend and even the risk to forget someone of them not processed.
So a feature that allows selecting all instances of a search string or a RegEx pattern will solve this issue in seconds instead of going for a workaround because Column Editor will work as well on selections that are spread around in the file even if not in column.
Also in find and replace dialog there is the possibility to apply a style to all matches but the use of that feature is less important than selection of all instances of matches because just highlighting them with a nice color do not allow any edit per se.
-
-
Sorry, I didn’t follow any of that… at all. :-(
-
@Alan-Kilborn said in Automatic selection of ALL instances of a searched string, in one go:
Sorry, I didn’t follow any of that… at all. :-(
Please clarify, I am not getting what you mean, sorry
-
If I understand you correctly, you want something like this mock?
A way to find and select each instance to work in multi-editor/instance mode, right?
Yes, that would be beneficial, but I think it would also open a can of worms. I already see that some select 1_000_000 instances of a word. -
@Ekopalypse
Yes that would be beneficial indeed IMHO too.IMHO no need of the “only” word, just Select all will work fine
Of course everything could have drawbacks when used improperly: even regular expressions if improperly used can open not just one but multiple cans or worms.
Also into the dialog the button Count can help to prevent problems. And I suppose that the tools present into the tab Mark will help too for the same reason to check before act.
-
@Ekopalypse said in Automatic selection of ALL instances of a searched string, in one go:
If I understand you correctly, you want something like this mock?
For the record, I understand exactly what @wonkawilly wants.
But I wanted him to explain why he thinks having such a capability is so advantageous. I wanted him to do this on his own, so I wasn’t leading him, but he couldn’t figure out what I was asking for, so…What are you going to do when you have all these matches as selected text, that you can’t do already in the N++ search/replace/mark interface, without having text selected as a result of a search?
Your choices are:
-
start typing (this will replace the selected text with what you type; hmmm, there’s already a Replace function that will do this)
-
copy (there’s already a Copy Marked Text function – ok, you have to mark text first)
-
delete (just a Replace with nothing, use that function)
-
cut (copy plus delete, a bit laborious with existing functionality, but is this a really common need?)
-
scroll around post-search to “eyeball” all the matches (just use Mark All; also, if text was selected and you do this, be careful that you don’t do a keyboard/mouse action that causes all of your selected text to become immediately unselected)
In short, no big advantage that I see to having text selected as a result of a search. Sure, it is fine to want it, but perhaps there is a reason it doesn’t already exist.
-
-
It serves a similar but different purpose, in the end you have several cursors to work with. At the moment, the Find and Replace dialog cannot do this and the options mentioned are, for this case, also no options, because they leave only one cursor after the actions.
-
@Ekopalypse said in Automatic selection of ALL instances of a searched string, in one go:
in the end you have several cursors to work with
And the big advantage to that is…?
-
Hi, @alan-kilborn and All,
Alan, you asked @wonkawilly to describe the benefits of this potential new feature but, to be honest, I said exactly the same thing in my first post when I added, at the end :
So, I wonder if we should propose an enhancement of the
Mark
dialog, with a new box optionSelection of the Occurrences
or equivalent, using this mechanism ?So, you would also be entitled to level the same criticism at me and ask me to justify the need for multiple cursors !
Thus, I read your last post carefully and, of course, your arguments seem relevant. However, I have an intuitive feeling that this new functionality would be very powerful, especially when you decide to take consecutive actions on this selection of cursors !
For example, considering the text :
This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test This is a simple test
-
Let’s say we’ve selected the ten words
simple
, using the latest version of theMutliple-Selections.py
script. -
First, we decide to delete the word
simple
: We could use the Replace dialog with the search ofsimple
,Nothing
as the replacement and with theIn selection
box ticked, to obtain the following text :
This is a test This is a test This is a test This is a test This is a test This is a test This is a test This is a test This is a test This is a test
- However, suppose we change our mind and we want to add
10
line-breaks, AFTER deleting those10
simple words. Now, using the Replace dialogue is not so easy, because we have to search for the stringa test
and change it to the stringa \r\n test
, just for those ten lines ! If we had used the multiple cursors’ technique, we would only have had to press theEnter
key :-)
And then, after all: if
Neil Hodgson
, and his team of devs, have added theSCI_MULTIPLESELECTADDNEXT
andSCI_MULTIPLESELECTADDEACH
commands, in the Scintilla project, there must be some point in using them !Best Regards,
guy038
BTW, once I selected the ten lines of my test and used the
Multiple_Selections.py
script to select the ten wordssimple
and deleted them with theDel
key, I tried aCtrl + Z
action in the hope of get the wordssimple
back. But no success ! I suppose that my last version should include a sequenceeditor.beginUndoAction()
andeditor.endUndoAction()
… -
-
No worries; it was merely my attempt to point out that it probably wouldn’t be as useful as people think on first consideration.
Now consider the
Select all only
checkbox in the @Ekopalypse UI mockup. How is one to know which actions this applies to? Does one need it for Find Next? (no). Count? (maybe?). Find All in Current Document? (certainly). Find All in All Opened…? (probably?).The historical way of options that apply to only certain actions is to put them in a box that groups those actions; here we see In selection as the example of that. How would something like that best be done here, or is the
Select All Only
checkbox left floating out on its own?Would this apply to Find in Files as well, for documents that are already open?
Is there any effect for Mark? Maybe the new checkbox only appears on the marking tab, as this has some logical commonality.
It’s good to want things, but it is also good to consider why you want them, and then all the downstream concerns.