Sublime Forum

Select All on Mac slow on large files

#1

Seems like this is a pretty recent change (hard to keep up with all the updates!), but I’ve noticed Select All on files with 2000+ lines will cause the beachball spin for a good 10 seconds before selecting all the text. As I reduce the file by 500 lines or so, it speeds up incrementally until about 500 lines where there’s just a small lag.

Wondering if this might have anything to do with the recent change in selections now maintaining syntax highlighting?

I’m on a 2.26ghz C2D MBP with 4GB of memory.

On the other hand I’ve noticed other operations speeding up dramatically in recent releases, especially scrolling & navigating text.

0 Likes

#2

Something is seriously wrong if Select All is showing a beachball - at a fundamental level, it doesn’t do any more work than moving the cursor with the arrow key. For reference, Select All here on a file with 500k lines has no perceptible delay.

One possible cause is a plugin causing the slow down, by doing work in its on_selection_modified callback. To verify this, you can run with a clean installation:

  1. Quit Sublime Text 2
  2. Move ~/Library/Application Support/Sublime Text 2 to another location (all customizations are in this folder)
  3. Start Sublime Text 2, and everything will be as it was when first installed - verify if the issue is still here

You can restore your settings by reversing the above procedure: delete the new ~/Library/Application Support/Sublime Text 2 folder that will get created in step 3, and move your original one back.

0 Likes

#3

I was going to mention what I believed was the culprit, and you are 100% correct. I found it by starting with a fresh settings dir as you suggested, and testing after adding each plugin. It’s this plugin causing the hang:

"""
DRAW OUTLINED
	"word_highlights_draw_outlined" : true
		Add this to your user prefs to make the highlights be drawn as outlines
		instead of as filled highlights.

HIGHLIGHT WORDS WHEN SELECTION IS EMPTY
	"word_highlights_when_selection_is_empty" : true
		Add this to your user prefs to make words highlight when the insertion point
		is inside of them but they're not actually selected.

COLOR OPTIONS
	"word_highlights_color_scope_name" : "wordhighlight"
		Normally the color of the highlights is the same as the color of comments
		in your code. If you'd like to customize the color, add the below to your
		color scheme file and change EDF2E9 to whatever color you want, then add
		this to your user file preferences.
---ADD TEXT BELOW THIS LINE TO YOUR COLOR SCHEME FILE---
		<dict>
				<key>name</key>
				<string>WordHighlight</string>
				<key>scope</key>
				<string>wordhighlight</string>
				<key>settings</key>
				<dict>
					<key>foreground</key>
					<string>#EDF2E9</string>
			 </dict>
		</dict>
---ADD TEXT ABOVE THIS LINE TO YOUR COLOR SCHEME FILE---
"""

import sublime
import sublime_plugin

DEFAULT_COLOR_SCOPE_NAME = "comment"

def regex_escape(string):
	outstring = ""
	for c in string:
		if c != '\\':
			outstring += ''+c+']'
		else:
			outstring += '\\'
	return outstring

class WordHighlightListener(sublime_plugin.EventListener):
	def on_selection_modified(self,view):
		settings = view.settings()
		color_scope_name = settings.get('word_highlights_color_scope_name', DEFAULT_COLOR_SCOPE_NAME)
		draw_outlined = bool(settings.get('word_highlights_draw_outlined')) * sublime.DRAW_OUTLINED
		word_separators = settings.get('word_separators')
		
		regions = ]
		for sel in view.sel():
			#If we directly compare sel and view.word(sel), then in compares their
			#a and b values rather than their begin() and end() values. This means
			#that a leftward selection (with a > b) will never match the view.word()
			#of itself.
			#As a workaround, we compare the lengths instead.
			if len(sel) == len(view.word(sel)):
				string = view.substr(sel).strip()
				if len(string):
					regions += view.find_all('\\b'+regex_escape(string)+'\\b')
			elif len(sel) == 0 and bool(settings.get('word_highlights_when_selection_is_empty')):
				string = view.substr(view.word(sel)).strip()
				if len(string) and all([not c in word_separators for c in string]):
					regions += view.find_all('\\b'+regex_escape(string)+'\\b')
		view.add_regions("WordHighlight", regions, color_scope_name, draw_outlined)

I changed this line:

if len(sel) == len(view.word(sel)):

to:

if len(sel)<=100 and len(sel) == len(view.word(sel)):

and all is well now. Thanks again for the quick response.

0 Likes

#4

Is there a reason view.word() takes so long? It seems like it could work backward from the beginning of the selection and forward from the end and in either case just bail at a non-word character.

0 Likes

#5

That is the way view.word() operates. I suspect the slowdown comes from constructing or matching the very long regex that will be passed to view.find_all().

view.word(region) will expand the begin and end portions of the region to be on word boundaries - it’ll happily return a selection consisting of the whole file, for example, rather than containing just a single word. The method name doesn’t clearly indicate this, unfortunately.

A better check than:

if len(sel) == len(view.word(sel)):

would be:

if view.word(sel.a) == view.word(sel.b):

this would check that the selection is either a whole or portion of a single word, which I think was the original intention.

0 Likes

#6

Oh of course—when natebeaty said that adding “len(sel)<=100” fixed things, I assumed from glancing at the line that it fixed things by shortcutting the view.word() call, but it also obviously skipped over the entire body of the if statement.

The original intention was actually to check that an entire word was selected: i.e. if I select the variable “sel”, I would see all instances of it highlighted, but if I select the “sel” at the beginning of the word “select” in this sentence, I wouldn’t want any highlights.
I guess I could do this:

if len(sel) == len(view.words(sel.a))

That seems like it should work unless the word itself is really quite long.

0 Likes

#7

Really I think the best answer actually is just limiting the length of the input word like natebeaty did—I find that I actually like being able to select multiple consecutive words (like “settings.get”) and having all occurrences highlight.

0 Likes