Sublime Forum

Looping over all regions in view?

#1

I’m trying to write a small plugin that loops over each paragraph in the view (it’s a prose file, not code) and add a tab to the first line if it isn’t already present.

This is what I have so far, but the results are quite odd… I get tabs in the middle of paragraphs after the first.

import sublime
import sublime_plugin


class TabAllParagraphsCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		doc = sublime.Region(0, self.view.size())
		regions = self.view.lines(doc);
		for region in regions:
			if not region.empty():
				_region = self.view.substr(region)
				if not _region.startswith('\t'):
					_region = '\t' + _region
					self.view.replace(edit, region, _region)

Clearly, I’m missing a key concept or two here. Would someone be kind enough to clue me in?

0 Likes

#2

I think your problem is that you collect the list of regions and iterate over them in forward order.

The regions are just sets of offsets into the buffer that describe start and stop regions, so if you handle the first region by modifying the buffer, the offsets for the following regions are now wrong by the same amount that you changed the buffer, and the problem compounds as you go forward.

If you do something like for region in reversed(regions) to iterate over them from the bottom up, the changes you make don’t affect the results as you go along because you’re altering the offsets for regions you’ve already handled.

3 Likes

#3

@OdatNurd, Thank you! Not only did that fix it, it makes perfect sense.

0 Likes

#4

Just a small remark about your programming style: You should not create such deep statements. Instead of that you can just use continue to go to the next loop iteration:

import sublime
import sublime_plugin


class TabAllParagraphsCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        doc = sublime.Region(0, self.view.size())
        regions = self.view.lines(doc)
        for region in reversed(regions):
            if region.empty():
                continue
            content = self.view.substr(region)
            if content.startswith('\t'):
                continue
            content = '\t' + content
            self.view.replace(edit, region, content)

0 Likes

#5

@r-stein, Thank you for your concern, but I wouldn’t consider what I wrote as “deep” in any regard–the code is trivial in its complexity by any standard. I think the drill down in this case makes clear the only condition where the content will be modified. But that’s what is great about different styles and different perspectives. Thank you for taking the time to share yours. Cheers.

1 Like