Sublime Forum

Difference when multiple selection

#1

Hi,

Too often I have two or more selections, one off-screen, and I edit one, only to find I have edited both.

Sometimes this is precisely the behaviour I want, so no worries.

But sometimes it isn’t – I would like to know how I can have, for example, my highlight selected content differently highlight when it is one of two or more regions selected.

For example highlighting one selection follows the style for highlight_line, but highlight more than one and that style follows alternate rules, so I can instantly spot when I am about to edit and affect multiple selections.

Thanks for any help.

Cheers, Alan

0 Likes

#2

One way to do that would be a plugin like the following (see here if you’re unsure about how to use plugins):

import sublime
import sublime_plugin

import functools


class GiantCursorEventListener(sublime_plugin.ViewEventListener):
    pending = 0

    def on_selection_modified_async(self):
        self.pending += 1
        sublime.set_timeout_async(functools.partial(self.update_cursor), 500)

    def update_cursor(self):
        self.pending -= 1
        if self.pending != 0:
            return

        s = self.view.settings()

        # Count of selections now and the last time we were triggered. If they
        # are the same, leave.
        now = len(self.view.sel())
        then = s.get('_sel_count', -1)

        if now == then:
            return

        # Remove the setting on 1 selection, otherwise increase it.
        # Save this selection count, then erase or add the setting.
        s.set('_sel_count', now)
        if now == 1:
            s.erase("caret_extra_width")
        else:
            s.set("caret_extra_width", 5)

This will adjust the caret width while the number of cursors is >1, and put it back to normal when the cursor returns.

You could also adjust other settings here, such as making the cursor taller or smaller.

In the extreme it could swap between two different color schemes that each have different colors for the caret, though swapping the color scheme that often may not be a good idea for performance reasons.

4 Likes

Cursor change when multi-region
#3

Wow, thank you very much @OdatNurd - brilliant! Off to go try that :slight_smile:

0 Likes

#4

@OdatNurd thanks again, this works superbly. It seems such a useful part of the UX – to know more than one region is selected (and will be affected if you begin to edit), I am surprised it’s not part of the core.

I made two changes:

        # sublime.set_timeout_async(functools.partial(self.update_cursor), 500)
        sublime.set_timeout_async(functools.partial(self.update_cursor), 100)

and

        if now == 1:
            s.erase("caret_extra_width")
            s.set("caret_style", "solid")
        else:
            s.set("caret_extra_width", 3)
            s.set("caret_style", "blink")

I hope the 500ms down to 100ms is not “dangerous” i.e. that the time there was a good practice to allow other parts of ST to “keep up” and that it was just a sensible value to help denote that something happened. Assuming it’s the latter then I find 100ms to be snappy while also helping.

I like the 5 width cursor, but with blink added (wen I usually use solid allows an even less intrusive cursor width while keeping it super obvious.

Brilliant!

Thanks again - I will be subscribing to your YT channel.

0 Likes

#5

That looks really cool !

2 Likes

#6

The original value was a conservative estimate (probably too conservative). I was actually going to point out that you probably want to play with that a little bit to find a balance that fits your work style, but I forgot.

The event is going to trigger for every selection change (which includes the cursor moving, since a cursor is just a selection that’s not selecting any text yet) no matter what, so the delay is essentially just how long after the last change it should look to see if it needs to change something.

As such, the delay allows it to essentially “ignore” consecutive changes until you stop moving/selecting for a small period of time.

Glad I could help out, and thanks for the sub! :smiley:

0 Likes

#7

Sorry to hassle you again @OdatNurd – thanks for the explanation BTW – but I just noticed that if I select something then ctrl + d one or more times, the plugin works AOK. But if the selection process is initialed from a ctrl + f to find the initial string, then ctrl + d, the plugin is not run.

Presumably this selection can be expanded to also look for this (poorly educated guess! : ):

def on_selection_modified_async(self):

Thanks for any comment (and glad @Lozminda found this useful too : )

0 Likes

#8

Hmm, I can’t replicate that here in testing. As long as the ctrl+f highlights at least one thing, closing the panel and then using ctrl+d adds to the selection and it triggers.

Can you provide a specific keyboard sequence that causes this to happen? Maybe I’m skipping something that you’re doing. Though all else being equal, the event should trigger regardless of prior steps since it should always tell you when the selection changes.

1 Like

#9

Happily, I type:
ctrl+f <li> (at this point I see one li tag high-lit (selected) [and editable I had assumed] and all others in view outlined) ctrl+d.
That last ctrl+d results in a second li being high-lit and selected [and editable I had assumed].

Oh dear, sorry for wasting your time, I now see that what looked like selected (and I had assumed editable) tags, were just previews of what is about to be edited, when I hit esc and leave the Find field and return to affecting the content of the document.

I had not realized focus/the cursor remained in the Search field.

That esc returns me to the content and at that point your most-useful new UI kicks in and I see the visual cue that multiple items are selected.

So sorry! And so happy it is me and nothing else :slight_smile:

Thank you for your help @OdatNurd :slight_smile:

1 Like

#10

Slightly related (but really a cheeky tack-on question); do you know how, if I have used repeated ctrl+ds to select consecutive copies of a pattern, I can break out of multi cursor mode and leave the cursor on the most recent one I added a cursor to?

When I press esc focus / the cursor jumps back to the first one.

If no time to answer, no worries. If I should post this as a new Q then I’ll do that too.

Thanks very much in advance for any tips (I tried to find out but failed and it’s bugged me for a long time).

Cheers, Alan

PS: Your multi cursor identifying plugin is SO useful :slight_smile:

0 Likes

#11

The command that does this is single_selection, but as far as I’m aware it doesn’t take any arguments that specifies which selection to go back to. However, it’s easy enough to add via a plugin:

class SingleSelectionLastCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        # Save the last selection
        last_sel = self.view.sel()[-1]

        # Clear the selection state and then add back the saved 
        # selection as the new single selection.
        self.view.sel().clear()
        self.view.sel().add(last_sel)

        # Ensure that the cursor for the last selection is visible 
        # in the window.
        self.view.show(last_sel.b)

You can add this to the plugin from above or as it’s own plugin file (in which case, make sure to include the import statements from the other plugin to this one).

This defines a command named single_selection_last, which does what the internal single_selection command does, except that it does it for the last of the defined selections in the file instead of the first.

To put it in place, you need a key binding:

{ "keys": ["ctrl+escape"], "command": "single_selection_last", "context":
    [
        { "key": "num_selections", "operator": "not_equal", "operand": 1 }
    ]
},

Here I used ctrl+escape as the key, so that esc does the normal action and ctrl+esc does the new one; if you only ever want to go to the last selection, you can just use escape here. The context makes sure that the binding only takes effect when there is more than one selection, since esc can potentially do a few different things based on the current editing situation.

1 Like

#12

Wow, so helpful. Thank you yet again @OdatNurd.

So fast you beat me, I was coming back here to say I had failed with one plugin (that said it was ST3 only), but found a feature of MultiEditUtils I had not seen, shift+esc, which does what I wanted.

I already had MEU installed for it’s fab Use selections as fields.

But your post is nonetheless so helpful - I am beginning to learn a little more about this side of customizing ST and it’s jaw dropping. Thank you!

1 Like