Hacker News new | ask | show | jobs
by Arch-TK 602 days ago
> multiple cursors

But seriously though, why does everything these days need multiple cursors? It's a confusing visual gimmick in every scenario I've seen it implemented in. I'll take fully fleshed out structural regular expressions or even perl-re over multiple cursors any day. Combined with as vim's [c]onfirm flag you get all the benefits of multiple cursors without all the clunky downsides and weirdness.

14 comments

When I need to edit the code around next five instance of "foo", but not every instance in the open file, then I don't know how to do that with structural regular expression, but pressing ctr+d five times and then edit what I want is really straight forward.

And I need to edit around a limited version of foo way more often than I need to edit all instances.

I'd watch this twitch stream. Contemporary editors are like 5-axis CNC mills and I'm still banging the rocks together. My occasional use multiple cursors is very basic. Your strategy wouldn't even occur to me.

Ages ago, Suresh Bhavanani (sp?) studied (suboptimum) usage of office and CADD applications. He was hired by the Army Corps of Engineers to help determine why adoption of CADD hadn't resulted in either higher productivity or improved quality.

His theory, which I believe, is various people have various "strategies" of varying merit. But for some reason users didn't (couldn't) readily learn better techniques from each other. IIRC, His two proposals were 1) re-organize user interfaces around tasks, instead of a features and 2) update training materials emphasize strategies.

IIRC, he compared manual and digital drafting. He identified a bunch of implicit and explicit "strategies" for manual work. eg Using ink and mylar, skilled drafters would work right-to-left, to avoid smudging prior work. Many early adopters carried those manual strategies over to CADD. Suboptimal, right? So then Suresh identified a bunch of digital appropriate strategies, leveraging the new tool.

Suresh did the same for spreadsheets.

Any way, this is a too long ramble now ending with the point: It'd be nice to see how others organize their work. I'm sure there's a lot of low hanging fruit "strategies". All the day to day tricks that experts use that don't get captured and explained in our training materials.

In vim, to replace in the next 5 lines (not instances)

    :.,.+5s/findtext/replacetext/g
In neovim, you'll see a preview of the changes as you type the regexp. Neovim is really good.
It's mostly a matter of taste. I do use multiple cursors quite often in Sublime Text when needing to edit some CSV or JSON file. It's probably the feature of Sublime Text, why i'm still having the editor around. It work's extremely well.

I could type ":.,.+5s/findtext/replacetext/g" in vim (and remember the syntax!) or i just could do "ctrl-d, ctrl-d, ctrl-d, ctrl-d, ctrl-d, replacetext" and have a visual, immediate confirmation on what i am doing. But i bet you, that i'll press ctrl-d 5 times faster than you entering ":.,.+5s/".

You do not have to remember the syntax; you can use Vimscript and you can create a custom keybinding that prompts you for the "findtext" and "replacetext" strings, then apply the search-and-replace operation over the next 5 lines (number of lines is easily adjustable and can be prompted for it, too; it does not have to be hard-coded either).

Vim users don't have to choose between precision and ease of use as you can build macros or mappings that match ANY workflow, creating powerful, customized processes that minimize the need to remember exact syntax or type it all out, for example achieving results similar to multiple cursors but with Vim's inherent efficiency and scalability. So, while Sublime Text's multiple cursors are handy, Vim can match - and even exceed - this functionality with very little setup (emphasis: one time setup!).

(I use VSCodium (which I really enjoy) for Go, PHP, and Elixir; IntelliJ IDEA for Java and Kotlin, and I use both Emacs and Vim for everything else).

I more commonly solve a problem like this one with /findtext<CR>creplacetext<Esc>n.n.n.n.

Remembering the syntax is not a problem here. Learning it in the first place, on the other hand...

I do like multiple cursors though. I end up switching to VSCode a couple times a week to do things where it's the right tool for the job.

What about holding down the shift arrow, pressing the down arrow five times, then ctrl-r? In most IDEs replace with an active selection will default to replacing only in the selection.
I speak vim, but vastly prefer multiple cursors when the editing is less trivial. Say you want to turn

  "foo bar" # For the 1st line
  "baz qux" # For the 2nd line
into

  "bar" # For the foo key
  "qux" # For the baz key
With multi cursors, you could select foo and baz and then interactively edit the rest of the line. I'm capable of doing that with vim, but by the time I'd even settled on an approach for it, I'd already be done in the other editor.

I could use Emacs macros to do the same kind of thing, and I've probably used `C-x (` a zillion times to build procedures I could then run 30 more times on the following lines. For quick interaction, I still prefer multi cursors.

For this specific example it's a visual block select of <foo > / <baz > cut and then visual select of <1st > / <2nd > and paste. But that's relying on things being the right width.

This is two lines, if everything was a different length, I think by the time I got multiple cursors to do this, I would have long managed by just doing the same editing task twice (cut overwrite-paste then cut overwrite-paste).

If I had to do this for many more lines I would have just used a substitution:

    :s/\(.*\)"\(\w\+\) \(\w\+\)" # \(.*\)\(\d\+\w\+\).\*/\1"\3" # \4\2 key/
And yes, that's 100% unreadable, but it was easy to write with the preview mode on modern neovim.

Although in this case I think a macro would probably be what I would settle on. This macro would look like:

    ^wdw/the<ret>wvawpwciwkey<esc>j (<ret> and <esc> are ^M and ^[ but I replaced them to avoid confusion)
But these are already the motions you would need to use to keep the multiple cursors in alignment. The thing is though that if you screw up it's usually easier to unscrew yourself when it's just one line than when you've gone ahead and screwed up 20 times. Yes it's still just an undo but when I've used multiple cursors, especially when you're trying to break up lines, you're forced to deal with that breaking up happening in all cursors. Things quickly get overwhelming.

Multiple cursors are effectively (in my experience) just macro recording with automatic playback simultaneously at all the positions you care about. Whereas if you just record a macro and apply it wherever you would otherwise place your cursors, you get the same result without having the visual mess of dealing with multiple cursors. And it's just as error prone as macros.

For longer distance, I use search navigation, faster than scanning where to put the cursors.
Vim can do multiple cursors as long as they start on the same column: ff<ctrl-v>jdw
But then you'd have to count beforehand and have no good way to correct instead of just going off that sweet immediate visual feedback
Vim, and I'm sure neovim, allow visually selecting an area by lines. If you then type ":" to initiate a search and replace, it will apply only to those lines.
Why would I want to go via this roundabout way? What happens if I decide to add/remove one more line to the mix later?
Say I want to change "foo" to "bar" in a single function in my file. I hit "V" to enter by-line visual mode at the top of the function, go to the end of the function and type ":s/foo/bar/g". If there are other words in the function that contain foo as a substring, then ":s/\<foo\>/bar/g".

Please describe your non-roundabout way then.

I think even the people advocating these stupid keyboard combinations don't really use them... they just think that it makes them sound smart.
You would restrict your regexp to a region. In Emacs you just select a region somehow (like your press ctrl+d five times), then just do the regexp replace.
Notice that you also don't need to write a regex with multiple cursors - instead, you can visually see your selections and changes as you go, using the same editing motions that you would typically use.
Vim is a great editor, and when I see people laboriously clicking to create multiple cursors to do something that a simple s/foo/bar/g would accomplish, I feel sorry for them. However, multiple cursors can be pretty great; I love the implementation in kakoune, where I can make multiple selections and then replace them with the output of piping them through an arbitrary shell command. I like to do this with basic math I write out as inputs to dc and then transform to the computed results using select and pipe.
Would s/foo/bar/g change every foo into bar without also changing every food into bard?

Also in most use cases, clicking for each multicursor is the wrong way to do it.

With multicursor you can:

- Press a shortcut to auto select every instance of the characters that you already have highlighted (which is just s/foo/bar/g with visual feedback before confirming)

- Press your "select next occurrence" shortcut a few times so you're only changing the first few occurrences that you care about.

- Press your "select next occurrence" shortcut to select the instances you want and press your "skip next occurrence" shortcut to skip the instances you want to keep. That way you can change a bunch of "foo"s to "bar"s while keeping all "food"s as "food"s but also keeping those few instances of "foo" that you want to stay as "foo" (such as in comments, imports, tests, etc...)

- Press your "select next occurrence" shortcut (usually ctrl/cmd + d) each time if you literally want to see each instance before moving on to the next. Usually this is when you really want to make sure of something next to one of the occurrences.

- And finally clicking each cursor if there really is no simple pre-existing pattern for where you're trying to make changes

> Would s/foo/bar/g change every foo into bar without also changing every food into bard?

Sure, and if that’s the case you write a better pattern. The replacement s/foo/bar/g applies only to the current line as written. It’s easy to see if it will work or not. But if you also have food on the same line, you can write s/foo\ze\W/bar/g to match foo followed by a non-letter and replace only foo.

> Also in most use cases, clicking for each multicursor is the wrong way to do it.

Maybe, but I’ve sure spent a lot of time pair programming with people who do it, which is why I bring it up. For what it’s worth, I prefer multicursors, and my find-and-replace workflow is much as you describe, albeit entirely keyboard and regex driven. Your average junior programmer who just learned vscode three or four years ago only knows multiclicking though, and I don’t want to kill the momentum by interrupting to point out how they could use their tools better.

In vim, you can do s/foo/bar/gc to iteratively confirm or skip each replacement.
I use multiple cursors all the time and rarely use the mouse to create them. Either it's "Add cursor to line ends" (VSCode)/"Split into lines"(Sublime) or just Ctrl+D.
I use Vim every day, but sometimes it's simply more convenient to visually confirm how something should be aligned etc., which is where Visual Block mode comes in handy. And Visual Block mode is just multiple cursors limited to a single column, so I can definitely see how multi-cursor support can be a more modern evolution.
Can you give an example of using dc like this? Seems like something I would find really useful!
Absolutely!

Suppose I'm editing a text file, and I'm working through a tradeoff between data volume and accuracy, where more accuracy requires more data. I have an array of 14 sensors, and I can configure them to take either 100-byte samples or 500-byte samples, and I can take samples at intervals between 1 and 60 seconds.

So I make myself a little table:

    sensors size interval
    14      100  1
    14      100  30
    14      100  60
Then I duplicate it:

    sensors size interval
    14      100  1
    14      100  30
    14      100  60
    14      100  1
    14      100  30
    14      100  60
I'm using kakoune, so I can put my cursor on the 100 in the first duplicate row, press C twice, then press r5, and now I have this table:

    sensors size interval
    14      100  1
    14      100  30
    14      100  60
    14      500  1
    14      500  30
    14      500  60
Now, I want to know bytes per day, and this is where the math comes in. I start adding columns. First I duplicate the three existing columns, since I want to see them next to the result. I can do this by putting a cursor at the beginning of each row, highlighting the whole thing, and pasting. I also hit & to line up the pasted selections.

    sensors size interval
    14      100  1        14      100  1       
    14      100  30       14      100  30      
    14      100  60       14      100  60      
    14      500  1        14      500  1       
    14      500  30       14      500  30      
    14      500  60       14      500  60      
Now, because I don't want to have to think about the stack too hard, I put 1440 (minutes per day) and 60 (seconds per minute) in before the interval column.

    sensors size interval
    14      100  1        14      100  1440 60 1       
    14      100  30       14      100  1440 60 30      
    14      100  60       14      100  1440 60 60      
    14      500  1        14      500  1440 60 1       
    14      500  30       14      500  1440 60 30      
    14      500  60       14      500  1440 60 60      
Then I add my operations. And keep in mind I'm still using multiple cursors, so all this stuff is just getting typed one time.

    sensors size interval
    14      100  1        14      100  * 1440 * 60 * 1 / p      
    14      100  30       14      100  * 1440 * 60 * 30 / p     
    14      100  60       14      100  * 1440 * 60 * 60 / p     
    14      500  1        14      500  * 1440 * 60 * 1 / p      
    14      500  30       14      500  * 1440 * 60 * 30 / p     
    14      500  60       14      500  * 1440 * 60 * 60 / p     
Now each line has a little dc program on it, like 14 500 * 1440 * 60 * 60 / p.

I then highlight the back half of each row (again, still working with the same set of cursors the whole time, it takes way longer to explain this than it does to actually do it.) I type

    | dc
and each of my selections gets run through dc. The result is:

    sensors size interval
    14      100  1        120960000
    14      100  30       4032000
    14      100  60       2016000
    14      500  1        604800000
    14      500  30       20160000
    14      500  60       10080000
That's not super readable, so I cursor over three times (multiselection is still active!) and insert commas, and then I do it again:

    sensors size interval
    14      100  1        120,960,000
    14      100  30       4,032,000
    14      100  60       2,016,000
    14      500  1        604,800,000
    14      500  30       20,160,000
    14      500  60       10,080,000
The result is I've done a quick back-of-the-envelope calculation on how much data I need to handle in each scenario.
Thanks! This is pretty cool. I do often run shell commands on entire lines/ranges of lines in vim, but haven't tried it with just parts of lines. I just tried with vim's rectangular selection, and while it does correctly give the 'dc' output, it replaces entire lines with the output instead of just the selection. I'll have to look into this further.
Enjoy! Kakoune’s selection-verb editing model really clicked for me; I had been a heavy user of visual mode in vim before I switched. The great Unix integration composes really well with the selection model too.
Replying to my own post for a couple of follow on comments:

1. I wrote this comment using kakoune as the editor under w3m

2. I don’t know emacs as well as I’d like to. Anyone want to chime in on how you’d do this in emacs?

Some people can craft the perfect command ahead of time to execute an series of editing operations on the first try. Some other people (/raises hand) prefer to visual incremental feedback on an in-progress operation and be able to partially undo mistakes as they go.
With Vim it’s all local decision unless writing a macro or regex substitution would take less time (the structure is very repetitive). Often it’s just n.n.n.n. (Next search occurence and repeat last edit)
People coming from word or notepad have no idea what 'n.n.n.n.' is supposed to be - they know how a cursor works though.
I feel exactly the opposite, macros and regexps always felt like I was trying to shoot something blindfolded, it was always awkward and usually didn’t work the first time. Multiple cursors provide immediate visual feedback. Once I started using them they replaced macros and find-and-replace immediately. I don’t use an editor unless it supports multiple cursors. What “clunky downsides and weirdness” are you experiencing?
For the record, there are plugins (for e.g. Emacs) that provide visual feedback on regex operations.
Using :%s instead of :s lets you preview in real-time what a regex substitution will do while you are typing it.
Multiple cursors are just live visual feedback for editing macros. Generally live visual feedback is a universal good, e.g., I'd argue most innovations in desktop user interfaces for creative apps are using Moore's Law to take modal interfaces and make them live (and correspondingly, non-destructive). E.g., find-as-you-type search and fuzzy finders being a couple examples, as well as most changes to Photoshop, GPU-based rendering IPR views, the entire existence of Lightroom and Ableton Live as apps.

With that said, the reason live visual feedback haven't systemically replaced all previous modal interfaces, the way they have for media editing apps, is because it's easier to picture the result of a text editor (i.e., relative to a HSR change to a photo), so it's not quite as revolutionary for text editing as it is applied elsewhere, I'd still argue it's a universal good though, more visual feedback is always better.

I cannot imagine a workflow without multiple cursors. I use this feature dozens of times every single day. It became a subconscious part of how I edit text.
I find multiple cursors very useful, a must-have. However I think there needs to be more "safety" around them. It's very easy to accidentally have multiple cursors active without you knowing, and the next thing you type changed something you didn't intend to. I would appreciate if there was some sort of lock I can apply to multiple cursors, to only use them when I need them and when I'm aware that I'm using them.
> However I think there needs to be more "safety" around them. It's very easy to accidentally have multiple cursors active without you knowing ...

Relatedly, if you've created cursors across many lines (or more lines than fit in your viewport), and for whatever reason you want to move all the cursors to the start or end of lines, you want to press Home/End not once, but twice, due to to possible word wrapping on lines outside your view. I've lost nontrivial work by making this mistake!

Way less mental overhead than macros and much faster for most cases. You might wanna look at them beyond surface level, they\'re actually awesome.

Having both is even better. While I'm not a fan of Helix's selection→action model it's a great example of how macros&multicursors go very well together. Soon to be on neovim native too, so there's that

Multiple cursors aren't really any different from regex for editing, but are much easier for the layman to pick up and often much quicker for edits where the regex would be complicated and verbose. If my edit-scope is within a single screen or so, I will almost never use regex before multiple cursors. They are incredibly useful for writing new content though (just think about all the times text or code is structurally repetitive with lots of identical boilerplate).
I see multiple cursors as an alternative to macros rather than regexp. There's stuff that is really awkward with regexp, like anything involving multiple lines, for example. But more importantly, macros and multiple cursors have the opportunity to operate at a higher-level, ie. taking into account language syntax and editor modes etc. Regexp is just about text. Having said that when I see people using it they are just doing a glorified regexp replace more often than not.
I use multiple cursors all the time in Kakoune. Say I want to make a change that is very similar on 10 adjacent lines. I could write a regex or macro that does what I want, which will require me doing that with no visual feedback as I build it. Instead, I can put a cursor on each line and see if I’m getting it right as I’m making the changes. To me, it is an easier, more interactive way of doing the things you’d usually do with a macro in Vim.
I use multiple cursors multiple times per hour when programming. I do not like vim and I don't use regex. And I am convinced I am faster this way than I could ever be using vim or regex for common editing operations. They are ubiquitous but imho still underrated.
Why don't you use regex for all editing in the single-cursor case as well then?
If I want to change a word on one line, I find it faster to do <esc>cw replacetext rather than use the mouse to select the text and then replace text.

If I want to do that on 2-3 lines, I just use /findtext n and . to redo the prev command.

Anything more than that becomes a full search/replace.

Ymmv. It's just what I find efficient, given my muscle memory.

The point is that single-cursor editing is a special case of multiple-cursor editing. If there's value in any single-cursor editing operations that don't use regex (or other indirectness) then they have value when used with multiple cursors, too.

The mouse doesn't necessarily come into the discussion at all.

Two rules of success:

1. Be attractive

2. Don't be unattractive

Lack of multi cursor supprot is unattractive in current era.

Tom Brady agrees.