Extending the editor—searching with regular expressions

The editor already provides basic search and replace functionality but if you are used to other editors you might miss the possibility to search using regular expressions. This plug-in provides this functionality.

Regular expressions are very powerful and many programmers love their versatility (and many others loathe their poor readability). Whether you love or hate them, they are very expressive: matching any decimal number can simply be expressed as d+ for example (one or more digits). If you are looking for a word that is spelled differently in British or American English, such as colour/color, you can match any of them with the expression colou?r (color with an optional u).

The following code will show that Blender's built-in editor can be equipped with this useful search tool with just a few lines of code. The script provided should be installed in Blender's scripts directory and can then be invoked from the text editor menu as Text | Text Plugins | Regular Expression Search or by a hot key Alt + Ctrl + R. It will pop up a small input widget where the user may enter a regular expression (this pop up will remember the last regular expression entered) and if the user clicks on the OK button or hits Enter the cursor will be positioned at the first occurrence that matches the regular expression, highlighting the extent of the match.

Extending the editor—searching with regular expressions

To register the script as a text plug-in with the designated hot key the first lines of the script consist of the customary headers augmented with a Shortcut: entry (highlighted below):

#!BPY
"""
Name: 'Regular Expression Search'
Blender: 249
Group: 'TextPlugin'
Shortcut: 'Ctrl+Alt+R'

Tooltip: 'Find text matching a regular expression'
"""

The next step is to import the necessary modules. Python supplies us with a standard re module, which is well documented (the online docs are sufficient even for novice users unfamiliar with regular expressions), and we import Blender's bpy module. In this book we do not often use this module as it is marked as experimental, but in this case we need it to find out which text buffer is the active one:

from Blender import Draw,Text,Registry
import bpy
import re

To signal any error conditions, such as an illegal regular expression or when nothing matches, we define a simple popup() function:

def popup(msg):
Draw.PupMenu(msg+'%t|Ok')
return

Because we want to remember the last regular expression the user entered we will be using Blender's registry and, therefore, we define a key to use:

keyname = 'regex'

The run() function ties all functionality together; it retrieves the active text buffer and bails out if there isn't one:

def run():
txt = bpy.data.texts.active
if not txt: return

Subsequently, it retrieves the cursor position within this buffer:

row,col = txt.getCursorPos()

Before presenting the user with a pop up to enter a regular expression we check if we stored one earlier in the registry. We simply retrieve it and if it fails we set the default expression to the empty string (highlighted). Note that we do not pass any extra parameters to the GetKey() function because we do want to store any information on disk in this case. If the user enters an empty string we simply return without searching:

d=Registry.GetKey(keyname)
try:
default = d['regex']
except:
default = ''
pattern = Draw.PupStrInput('Regex: ',default,40)
if pattern == None or len(pattern) == 0 : return

We compile the regular expression to see if it's valid and if this fails we show a message and return:

try:
po = re.compile(pattern)
except:
popup('Illegal expression')
return

Now that we know the regular expression is correct, we iterate over all lines of the text buffer starting at the line the cursor is on (highlighted). For each line we match our compiled regular expression to the string (or the part after the cursor if it is the first line).

first = True
for string in txt.asLines(row):

if first :
string = string[col:]
mo = re.search(po,string)

If there is a match we note the start of the match within the line and the length of the match (suitably set off if it's the first line) and set the cursor position to the current line and the start of the match (highlighted). We also set the "select position" to the position of the match plus the length of the match so our match will be highlighted and then returned. If there is no match within the line we increment the row index and continue the iteration.

If there is nothing left to iterate over, we signal the user that we did not find any match. In all cases, we store the regular expression in the registry for reuse:

if mo != None :
i = mo.start()
l = mo.end()-i
if first :
i += col
txt.setCursorPos(row,i)

txt.setSelectPos(row,i+l)
break
row += 1
first = False
else :
popup('No match')
Registry.SetKey(keyname,{'regex':pattern})
if __name__ == '__main__':
run()

The full code is available as regex.py in regex.blend but should be installed in Blender's scripts directory with a suitable name, such as textplugin_regex.py.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.70.38