Home Download Buy Blog Forum Support

how to use back reference in replacement?

how to use back reference in replacement?

Postby origin008 on Wed Mar 05, 2014 7:15 am

i want to do some replacement with back reference,
such as the following code sample in my python file:
Code: Select all
……
ff=self.view.find_all("\s*(\w+)\s*")
ff.reverse()
for rr in ff:
      self.view.replace(edit, rr, "\1")

but the back reference seems not working.
how should i do?
thx!
origin008
 
Posts: 4
Joined: Wed Mar 05, 2014 7:02 am

Re: how to use back reference in replacement?

Postby FichteFoll on Tue Mar 11, 2014 3:39 am

It doesn't work like that.

`view.find_all` returns a list of Regions and does not care about capturing groups. It returns the start and end coordinates of the match.

You will have to use python's `re.sub` instead and extract the whole text before, do the replacement in your plugin and set the whole text back again. I actually remember answering this question a few ages ago, so maybe you will get more information by searching because I can't bother to write a code example currently, sorry.
FichteFoll
 
Posts: 388
Joined: Fri Mar 16, 2012 11:49 pm
Location: Germany

Re: how to use back reference in replacement?

Postby facelessuser on Tue Mar 11, 2014 4:00 am

You don't have to replace the entire buffer, but you do have to search it. I did a plugin, RegReplace, where you can specify custom searches you can string together, and like FichteFoll mentioned, I had to use re on the view's buffer. I basically wrote a function that would search the buffer and return the regions and the extractions (modified text after replace); like the Sublime API does. Then I could just iterate backwards through my sublime regions, tracking the index so I could access the corresponding extractions, and replace backwards. I don't know if you lose all of your selections when you replace the entire buffer...I didn't want to lose my selections, so I opted for replacing the regions in reverse order.

    def regex_findall(self, find, flags, replace, extractions, literal=False, sel=None):
regions = []
offset = 0
if sel is not None:
offset = sel.begin()
bfr = self.view.substr(sublime.Region(offset, sel.end()))
else:
bfr = self.view.substr(sublime.Region(0, self.view.size()))
flags |= re.MULTILINE
if literal:
find = re.escape(find)
for m in re.compile(find, flags).finditer(bfr):
regions.append(sublime.Region(offset + m.start(0), offset + m.end(0)))
extractions.append(m.expand(replace))
return regions


replace corresponds to extractions (I'm cutting out unrelated code here):
    def greedy_replace(self, find, replace, regions, scope_filter):
# Initialize replace

...

count = len(regions) - 1

...

for region in reversed(regions):

...

replaced += 1

...

self.view.replace(self.edit, region, replace[count])
count -= 1
return replaced


Anyways, just showing a way to approach it.
facelessuser
 
Posts: 1570
Joined: Tue Apr 05, 2011 7:38 pm

Re: how to use back reference in replacement?

Postby duy on Wed Mar 12, 2014 1:25 am

Maybee I'm oversimplifying things, but since sublime text already does the find for you, all you have to do is loop over the selections and use regex to replace the text:

Code: Select all
[view.replace(edit, region, re.sub(pattern, r'\1', view.substr(region))) for region in reversed(list(view.find_all(pattern)))]


for clarification, this should do the same:

Code: Select all
    pattern = 'value="(.*?)"'
    for region in reversed(list(view.find_all(pattern))):
        current_text = view.substr(region)
        new_text = re.sub(pattern, r'\1', current_text)
        view.replace(edit, region, new_text)


re.sub() will only replace text where pattern applies and returns the original string otherwise
duy
 
Posts: 12
Joined: Mon Mar 10, 2014 12:28 am

Re: how to use back reference in replacement?

Postby origin008 on Wed Mar 12, 2014 2:42 pm

great thanks to you all!
origin008
 
Posts: 4
Joined: Wed Mar 05, 2014 7:02 am

Re: how to use back reference in replacement?

Postby facelessuser on Wed Mar 12, 2014 5:05 pm

duy wrote:Maybee I'm oversimplifying things, but since sublime text already does the find for you, all you have to do is loop over the selections and use regex to replace the text:

Code: Select all
[view.replace(edit, region, re.sub(pattern, r'\1', view.substr(region))) for region in reversed(list(view.find_all(pattern)))]


for clarification, this should do the same:

Code: Select all
    pattern = 'value="(.*?)"'
    for region in reversed(list(view.find_all(pattern))):
        current_text = view.substr(region)
        new_text = re.sub(pattern, r'\1', current_text)
        view.replace(edit, region, new_text)


re.sub() will only replace text where pattern applies and returns the original string otherwise

It all depends on what you are doing. The example above requires you to search the same text multiple times. That isn't necessarily a problem, but I don't think it is necessary. Also, depending on how your original search is defined, you may have to modify it for the secondary search. Let's say you are using lookaheads or lookbehinds, the return won't include the non-capturing lookaheads and lookbehinds. When you do the subsearch the original regex will no longer be valid for your return text.
facelessuser
 
Posts: 1570
Joined: Tue Apr 05, 2011 7:38 pm


Return to Plugin Development

Who is online

Users browsing this forum: No registered users and 5 guests