Sublime Forum

Multiple cursors for syntactic vs text matches

#1

When selecting a variable (or similar) in a file, the editor’s highlighting will differ from the multiple-cursor behaviour of Ctrl+D.

I was wondering what the thoughts were on changing that, or if there’s another command I could bind to make them the same.
To demonstrate, some code:

When I select the proj variable on line 130, Sublime will helpfully highlight the next instance of the same variable on line 131:

Yet, pressing Ctrl+D (the “find_under_expand” command) will select the next matching string, which isn’t quite what I expected:

As you can see, instead of selecting the proj variable on line 131, it has instead selected part of the ‘project’ string on line 130.

After some research, I found a few threads about this before, which don’t solve the problem but do note that there’s magic behaviour based on if you currently have a selection active. This is a reasonable workaround, but my usual editing flow doesn’t work like this - I’m typically viewing each instance of a given selection, then wanting to edit all of them.

Is there a way to add cursors based on the syntactic highlighting?
Is there a different command I could bind?

Thanks! :smile:

0 Likes

#2

Bump :smile:

0 Likes

#3

I agree, this is one of the situations where ctrl+d is really not that helpful. It would have been better if the preview worked like what actually happens when you press ctrl+d again (depending on this magic that you mentioned), but it doesn’t and this is a rather frustrating issue.

There might also be some techniques involved that allow to always match the exact selected string, but I don’t know of any. I currently resort back to using “ctrl+k,ctrl+d” to skip the last selection and continue with the next instead.

0 Likes

#4

Agreed, very annoying. Is there a github issue on this?

0 Likes

#5

I hope, that i’ve correctly understand your problem.
You need to multiselect the word from cursor by its word boundary.
Try this plugin. The word from cursor position will detect, all occurences with the same scope will searched and you can choose to make full selections or set multiple cursor to start or end of the words. With another parameter you set an pattern with allowed characters for the word. Internal will this pattern use the flag re.IGNORECASE. The default pattern is set to “[a-z0-9_]”.

Edit 03.10.2015: I’ve found an error by detecting correct boundary - fixed now.

import sublime
import sublime_plugin
import re
import string

'''  word from cursor:
full selection of all occurences
    { "keys": "KEYSEQUENCE"], "command": "my_select", "args": {"select": "full", "pattern": "PATTERN_ALLOWED_CHARS"}},

multiple cursors set to word-begin of all occurences
    { "keys": "KEYSEQUENCE"], "command": "my_select", "args": {"select": "begin", "pattern": "PATTERN_ALLOWED_CHARS"}},

multiple cursors set to word-end of all occurences
    { "keys": "KEYSEQUENCE"], "command": "my_select", "args": {"select": "end", "pattern": "PATTERN_ALLOWED_CHARS"}},

PATTERN_ALLOWED_CHARS - i.e. "[a-z0-9_]" or "\\$a-z0-9_]"
'''
class MySelectCommand(sublime_plugin.TextCommand):
    def run(self, edit, select='full', pattern='[a-z0-9_]'):
        view = sublime.active_window().active_view()
        # get the current word
        searchword, scope = self.word_expand_by_pattern(view, pattern)
        if searchword == '': return

        # escape magic characters if any
        magic = ('\\','.','^','$','*','+','?')
        for char in magic:
            searchword = searchword.replace(char, '\\' + char)

        # search all occurences
        match = view.find_all(searchword)
        view.sel().clear()
        for reg in match:
            if (not re.search(pattern, view.substr(reg.a -1)) and
            not re.search(pattern, view.substr(reg.b +1))) and \
            view.scope_name(reg.a) == scope:
                if select == 'full':
                    view.sel().add(reg)
                if select == 'begin':
                    view.sel().add(sublime.Region(reg.a,reg.a))
                elif select == 'end':
                    view.sel().add(sublime.Region(reg.b,reg.b))


    def word_expand_by_pattern(self, view, pattern):
        '''
        Returns the word that is touched by the cursor and the scope.
        The pattern determines the allowed word-characters.
        If not compiled, the pattern will be compiled with flag re.I.
        '''
        if type(pattern) != '_sre.SRE_Pattern':
            pattern = re.compile(pattern, re.I)
        caret = view.sel()[0].begin()
        scope = view.scope_name(caret)
        word = ''
        while re.search(pattern, view.substr(caret -1)):
            caret -= 1
        while re.search(pattern, view.substr(caret)):
            word = '%s%s' % (word, view.substr(caret))
            caret += 1
        return word, scope
0 Likes