Multiple selections #8814

Closed
marijnh wants to merge 47 commits from github/fork/twiss/multiple-selections into master
marijnh commented 2013-07-06 13:14:55 +02:00 (Migrated from gitlab.com)

See Issue #778. Not everything is perfect yet, but it works quite well.
Todo:

  • testing on a mac
  • block selecting across the edges doesn't scroll the editor
  • multiple block selections (alt+drag, release, ctrl+alt+drag)
  • smarter copy & paste
  • keyboard shortcuts
  • autocomplete
  • clearer styling of adjacent selections (with a border or rounded corners), although showCursorWhenSelecting also helps
  • the new code in adjustPos is probably wrong (the second branch, which adjacent selections need, breaks the vim and emacs modes, but I don't know vim or emacs so I'm not sure when or why. with a single selection everything is fine though)
See Issue #778. Not everything is perfect yet, but it works quite well. Todo: - [ ] testing on a mac - [x] block selecting across the edges doesn't scroll the editor - [x] multiple block selections (alt+drag, release, ctrl+alt+drag) - [x] smarter copy & paste - [ ] keyboard shortcuts - [x] autocomplete - [ ] clearer styling of adjacent selections (with a border or rounded corners), although showCursorWhenSelecting also helps - [ ] the new code in adjustPos is probably wrong (the second branch, which adjacent selections need, breaks the vim and emacs modes, but I don't know vim or emacs so I'm not sure when or why. with a single selection everything is fine though)
marijnh commented 2013-07-08 12:28:56 +02:00 (Migrated from gitlab.com)

Hey, I just wanted to acknowledge this for now. I've seen it, and on the whole, it looks good. I'd prefer if you factored some of the functions that become much larger with your changes into smaller functions, but my original code was also guilty of huge functions, so it's not an important point, use your judgement.

The way cursors are drawn and maintained is probably cleaner if you just put them all in a single cursors div, clear it before redrawing the selection, and make that blink as a whole. That'd also make the order of the dom elements (which influences drawing order) clearer.

I'll give some more though on how the existing API should interact with the presence of multiple selections. Backward compatibility is a must, of course, but it'd be nice if it were easy for addon writers to just do the simple thing and still have their code work in a multi-selection situation.

I will review the code more closely soon.

Hey, I just wanted to acknowledge this for now. I've seen it, and on the whole, it looks good. I'd prefer if you factored some of the functions that become much larger with your changes into smaller functions, but my original code was also guilty of huge functions, so it's not an important point, use your judgement. The way cursors are drawn and maintained is probably cleaner if you just put them all in a single `cursors` div, clear it before redrawing the selection, and make that blink as a whole. That'd also make the order of the dom elements (which influences drawing order) clearer. I'll give some more though on how the existing API should interact with the presence of multiple selections. Backward compatibility is a must, of course, but it'd be nice if it were easy for addon writers to just do the simple thing and still have their code work in a multi-selection situation. I will review the code more closely soon.
marijnh commented 2013-07-08 17:57:03 +02:00 (Migrated from gitlab.com)

Thanks, that's good to hear! I'll do those things.

Thanks, that's good to hear! I'll do those things.
marijnh commented 2013-07-11 10:30:29 +02:00 (Migrated from gitlab.com)

Great progress. Is there a reason selections aren't kept in sorted order all the time? That'd save some of the copying and re-sorting.

By the way, github doesn't notify me of new commits pushed to a pull request. So when you think this is ready to merge, or want me to review something, add a comment.

Great progress. Is there a reason selections aren't kept in sorted order all the time? That'd save some of the copying and re-sorting. By the way, github doesn't notify me of new commits pushed to a pull request. So when you think this is ready to merge, or want me to review something, add a comment.
marijnh commented 2013-07-11 18:50:20 +02:00 (Migrated from gitlab.com)

Not really, I changed it. The copying still happens though, because I don't want to merge the selections while selecting.

I'll let you know.

Not really, I changed it. The copying still happens though, because I don't want to merge the selections while selecting. I'll let you know.
marijnh commented 2013-07-12 19:49:42 +02:00 (Migrated from gitlab.com)

Marijn, what do you think about the selection drawing stuff? (7c1f088, 8c9d277 and 76b15f2) It does make it very clear what you've selected and it looks a bit more polished too, but the code is a real mess, and I'm not even sure I like how it looks, so maybe I should just drop it.

Marijn, what do you think about the selection drawing stuff? (7c1f088, 8c9d277 and 76b15f2) It does make it very clear what you've selected and it looks a bit more polished too, but the code is a real mess, and I'm not even sure I like how it looks, so maybe I should just drop it.
marijnh commented 2013-07-15 22:03:55 +02:00 (Migrated from gitlab.com)

Whoo! @twiss - thank you SOOO much for working on this! I have really been looking forward to seeing this feature added to CodeMirror.

Whoo! `@twiss` - thank you SOOO much for working on this! I have really been looking forward to seeing this feature added to CodeMirror.
marijnh commented 2013-07-16 19:21:51 +02:00 (Migrated from gitlab.com)

mentioned in issue #4472

mentioned in issue #4472
marijnh commented 2013-07-16 22:26:42 +02:00 (Migrated from gitlab.com)

@slang800 Glad you like it!

`@slang800` Glad you like it!
marijnh commented 2013-07-17 14:17:45 +02:00 (Migrated from gitlab.com)

I'm on holiday right now, but will definitely spend time on this when I get
back.
On Jul 16, 2013 10:27 PM, "Daniel Huigens" notifications@github.com wrote:

@slang800 https://github.com/slang800 Glad you like it!


Reply to this email directly or view it on GitHubhttps://github.com/marijnh/CodeMirror/pull/1656#issuecomment-21070954
.

I'm on holiday right now, but will definitely spend time on this when I get back. On Jul 16, 2013 10:27 PM, "Daniel Huigens" notifications@github.com wrote: > `@slang800` https://github.com/slang800 Glad you like it! > > — > Reply to this email directly or view it on GitHubhttps://github.com/marijnh/CodeMirror/pull/1656#issuecomment-21070954 > .
marijnh commented 2013-07-25 18:05:57 +02:00 (Migrated from gitlab.com)

Selection drawing is still quite problematic. I think the border might be too much, both in terms of visual noise compared to previous versions, and in terms of the trickiness of getting it right. It currently looks off on both FF and Chrome in Linux (didn't try any other browsers). I was able to fix this by adding border-box sizing rules and a few pixels here and there, but selections that continue across a single line without actually touching still look completely wrong, and I expect more headaches to come up. Probably not worth it.

Selection drawing is still quite problematic. I think the border might be too much, both in terms of visual noise compared to previous versions, and in terms of the trickiness of getting it right. It currently looks off on both FF and Chrome in Linux (didn't try any other browsers). I was able to fix this by adding border-box sizing rules and a few pixels here and there, but selections that continue across a single line without actually touching still look completely wrong, and I expect more headaches to come up. Probably not worth it.
marijnh commented 2013-07-25 18:06:48 +02:00 (Migrated from gitlab.com)

Why remove this?

Why remove this?
marijnh commented 2013-07-25 18:10:28 +02:00 (Migrated from gitlab.com)

I don't see how this won't often leave the cache size at a too-low value.

I don't see how this won't often leave the cache size at a too-low value.
marijnh commented 2013-07-25 18:12:07 +02:00 (Migrated from gitlab.com)

It would be great if you could put unrelated fixes / changes in different pull requests, so that we only have the actual selection stuff to discuss here.

It would be great if you could put unrelated fixes / changes in different pull requests, so that we only have the actual selection stuff to discuss here.
marijnh commented 2013-07-25 18:16:49 +02:00 (Migrated from gitlab.com)

This appears to correspond to display.cursorDiv.childNodes at all times. Do we need it?

This appears to correspond to `display.cursorDiv.childNodes` at all times. Do we need it?
marijnh commented 2013-07-25 18:17:47 +02:00 (Migrated from gitlab.com)

This is a nit, but I'd prefer if you'd follow my style of only adding braces when necessary (or in an else chain when one of the branches needs braces)

This is a nit, but I'd prefer if you'd follow my style of only adding braces when necessary (or in an `else` chain when one of the branches needs braces)
marijnh commented 2013-07-25 18:19:05 +02:00 (Migrated from gitlab.com)

Let's put this class on the wrapper of the cursors instead. Easier to update.

Let's put this class on the wrapper of the cursors instead. Easier to update.
marijnh commented 2013-07-25 18:22:42 +02:00 (Migrated from gitlab.com)

Well, here slightly scary code became full-on terrifying. See my other comment about the borders on the selection. Looking at this, I'm very suspicious that there are a bunch of bugs hiding in it. This is exactly what bug-hiding code looks like. We should somehow simplify it. I just don't know how yet ;)

Well, here slightly scary code became full-on terrifying. See my other comment about the borders on the selection. Looking at this, I'm _very_ suspicious that there are a bunch of bugs hiding in it. This is exactly what bug-hiding code looks like. We should somehow simplify it. I just don't know how yet ;)
marijnh commented 2013-07-25 18:24:07 +02:00 (Migrated from gitlab.com)

I'm about to relent on this -- it's a bug-masking change, and I prefer for bugs to be noticeable... but yes, infinite loops are also bad. Very bad, for the user. In any case the change definitely doesn't belong in this pull request.

I'm about to relent on this -- it's a bug-masking change, and I prefer for bugs to be noticeable... but yes, infinite loops are also bad. _Very bad_, for the user. In any case the change definitely doesn't belong in this pull request.
marijnh commented 2013-07-25 18:25:49 +02:00 (Migrated from gitlab.com)

(Also, I think you should clip the length of the cache array itself as well when you change this.)

(Also, I think you should clip the length of the cache array itself as well when you change this.)
marijnh commented 2013-07-25 18:27:20 +02:00 (Migrated from gitlab.com)

IE7 isn't really supported anymore, so feel free to kill the try kludge.

IE7 isn't really supported anymore, so feel free to kill the `try` kludge.
marijnh commented 2013-07-25 18:34:39 +02:00 (Migrated from gitlab.com)

Could you write a bit more (possibly as a comment in the code) on how selecting and merging of selections is supposed to work? How does doc.unmergedSelections relate to doc.selections? What does doc.unmergedSel do? What exactly are the various ways in which multiple selections can occur? Why does merging the selections only have to look at adjacent ones? I'm a bit too fuzzy on this to properly review the code at the moment.

Could you write a bit more (possibly as a comment in the code) on how selecting and merging of selections is supposed to work? How does `doc.unmergedSelections` relate to `doc.selections`? What does `doc.unmergedSel` do? What exactly are the various ways in which multiple selections can occur? Why does merging the selections only have to look at adjacent ones? I'm a bit too fuzzy on this to properly review the code at the moment.
marijnh commented 2013-07-25 18:36:56 +02:00 (Migrated from gitlab.com)

Why is movingUp an exceptional situation?

Why is `movingUp` an exceptional situation?
marijnh commented 2013-07-25 18:40:45 +02:00 (Migrated from gitlab.com)

A comment with the intention of this block would be good. I guess it's a heuristic for whether the paste should be divided over the selections or repeated, but I can't figure out the exact idea.

A comment with the intention of this block would be good. I guess it's a heuristic for whether the paste should be divided over the selections or repeated, but I can't figure out the exact idea.
marijnh commented 2013-07-25 18:51:19 +02:00 (Migrated from gitlab.com)

The implications for the public API are actually not as severe as I expected. On the whole, I think your extensions of the API are quite uncontroversial. One thing I'm not clear about yet is whether we want to preserve 'handles' to selections (allowing querying and updating of a specific selection range through the API), and how the primary selection should be determined. It seems that it is currently always the last one that was made. Was that a conscious choice or a random effect of how the code works?

Tangentially, should doc.sel remain a thing, or would it be cleaner to always have code that accesses a selection be explicit about which one it wants to touch?

The implications for the public API are actually not as severe as I expected. On the whole, I think your extensions of the API are quite uncontroversial. One thing I'm not clear about yet is whether we want to preserve 'handles' to selections (allowing querying and updating of a specific selection range through the API), and how the primary selection should be determined. It seems that it is currently always the last one that was made. Was that a conscious choice or a random effect of how the code works? Tangentially, should `doc.sel` remain a thing, or would it be cleaner to always have code that accesses a selection be explicit about which one it wants to touch?
marijnh commented 2013-07-27 16:09:05 +02:00 (Migrated from gitlab.com)

Thanks for the feedback. I'm on a holiday as well, I'll reply when I'm back.

Thanks for the feedback. I'm on a holiday as well, I'll reply when I'm back.
marijnh commented 2013-08-10 14:33:13 +02:00 (Migrated from gitlab.com)

Fixed in 655e902.

Fixed in 655e902.
marijnh commented 2013-08-10 14:51:29 +02:00 (Migrated from gitlab.com)

Moved to #1734.

Moved to #1734.
marijnh commented 2013-08-10 15:04:58 +02:00 (Migrated from gitlab.com)

Fixed in 962a969.

Fixed in 962a969.
marijnh commented 2013-08-10 15:10:16 +02:00 (Migrated from gitlab.com)

Yes, except that it didn't contain secondary cursors (I tried to continue to do the same thing). Removed in 962a969.

Yes, except that it didn't contain secondary cursors (I tried to continue to do the same thing). Removed in 962a969.
marijnh commented 2013-08-10 15:18:30 +02:00 (Migrated from gitlab.com)

I made this change against an older version of CodeMirror, so I'm not even sure if it still does anything. Reverted in e2e0122.

I made this change against an older version of CodeMirror, so I'm not even sure if it still does anything. Reverted in e2e0122.
marijnh commented 2013-08-10 17:42:25 +02:00 (Migrated from gitlab.com)

The implications for the public API are actually not as severe as I expected. On the whole, I think your extensions of the API are quite uncontroversial.

That's good to hear, I hope the same goes for 0fc33fb. Another idea would be to be able to replace command names like "goCharRight" with "each.goCharRight" everywhere, which eliminates the need for every addon to be updated, and is cleaner too, but not, I think, flexible enough on its own.

One thing I'm not clear about yet is whether we want to preserve 'handles' to selections (allowing querying and updating of a specific selection range through the API),

I'm not sure when someone would need that, but we could pass the selection index to the function in eachSelection() to make things like that easier to do.

and how the primary selection should be determined. It seems that it is currently always the last one that was made. Was that a conscious choice or a random effect of how the code works?

My idea was to not have a primary selection at all, so doc.sel is now purely the current selection inside eachSelection(). The code, in eachSelection and elsewhere, that tries to keep doc.sel consistent is only there because some code seems to depend on that, but I didn't investigate yet. doc.unmergedSel is also part of that. However, because of that, the code to have and maintain a primary selection is already mostly there and now that I'm thinking more about it it seems like a good idea to give editors the option.

Tangentially, should doc.sel remain a thing, or would it be cleaner to always have code that accesses a selection be explicit about which one it wants to touch?

Well, that would be cleaner for the functions that don't do anything sensible (yet?) outside eachSelection() (e.g. setSelection, extendSelection, getSelection), but that's not all of them. Maybe it would be easier to just throw if those functions are called outside eachSelection?

> The implications for the public API are actually not as severe as I expected. On the whole, I think your extensions of the API are quite uncontroversial. That's good to hear, I hope the same goes for 0fc33fb. Another idea would be to be able to replace command names like `"goCharRight"` with `"each.goCharRight"` everywhere, which eliminates the need for every addon to be updated, and is cleaner too, but not, I think, flexible enough on its own. > One thing I'm not clear about yet is whether we want to preserve 'handles' to selections (allowing querying and updating of a specific selection range through the API), I'm not sure when someone would need that, but we could pass the selection index to the function in `eachSelection()` to make things like that easier to do. > and how the primary selection should be determined. It seems that it is currently always the last one that was made. Was that a conscious choice or a random effect of how the code works? My idea was to not have a primary selection at all, so `doc.sel` is now purely the current selection inside `eachSelection()`. The code, in `eachSelection` and elsewhere, that tries to keep `doc.sel` consistent is only there because some code seems to depend on that, but I didn't investigate yet. `doc.unmergedSel` is also part of that. However, because of that, the code to have and maintain a primary selection is already mostly there and now that I'm thinking more about it it seems like a good idea to give editors the option. > Tangentially, should doc.sel remain a thing, or would it be cleaner to always have code that accesses a selection be explicit about which one it wants to touch? Well, that would be cleaner for the functions that don't do anything sensible (yet?) outside `eachSelection()` (e.g. `setSelection`, `extendSelection`, `getSelection`), but that's not all of them. Maybe it would be easier to just throw if those functions are called outside `eachSelection`?
marijnh commented 2013-08-10 17:57:45 +02:00 (Migrated from gitlab.com)

I wanted a consistent behaviour that didn't depend on a primary selection, and it seemed like a good idea to be able to take a look at all selections, so now it scrolls to the first selection when movingUp, the last otherwise. I agree it's bad, do you have a better idea?

I wanted a consistent behaviour that didn't depend on a primary selection, and it seemed like a good idea to be able to take a look at all selections, so now it scrolls to the first selection when `movingUp`, the last otherwise. I agree it's bad, do you have a better idea?
marijnh commented 2013-08-10 18:57:41 +02:00 (Migrated from gitlab.com)

Added in 3616489.

Added in 3616489.
marijnh commented 2013-08-10 19:17:36 +02:00 (Migrated from gitlab.com)

No reason, fixed in 7254213.

No reason, fixed in 7254213.
marijnh commented 2013-08-10 19:31:46 +02:00 (Migrated from gitlab.com)

Fixed in 1993a05, if there's more I've missed please let me know.

Fixed in 1993a05, if there's more I've missed please let me know.
marijnh commented 2013-08-12 11:54:04 +02:00 (Migrated from gitlab.com)

Maybe have the most recent selection be the primary one, and always scroll that into view?

Maybe have the most recent selection be the primary one, and always scroll that into view?
marijnh commented 2013-08-12 12:12:41 +02:00 (Migrated from gitlab.com)

I find the concept of setting .sel, and the general API of eachSelection quite a bit too magical.

How about this:

  • The last selection made is the primary selection.
  • The old-style selection methods act on this selection. (with setSelection clearing all other selections)
  • We introduce a Selection class that is used to represent selections. addSelection returns an instance.
  • We add methods get, replace, find, move, extend, and clear to this class.
  • eachSelection passes the selection object to its argument function.

That requires a bit more changes to existing code to properly conform to the multi-selection API, but gives more control, and is easier to reason about.

What is postponeEndOperation? I'm really worried about breaking the invariant that an operation wraps a dynamic scope.

I find the concept of setting `.sel`, and the general API of `eachSelection` quite a bit too magical. How about this: - The last selection made is the primary selection. - The old-style selection methods act on this selection. (with `setSelection` clearing all other selections) - We introduce a `Selection` class that is used to represent selections. `addSelection` returns an instance. - We add methods `get`, `replace`, `find`, `move`, `extend`, and `clear` to this class. - `eachSelection` passes the selection object to its argument function. That requires a bit more changes to existing code to properly conform to the multi-selection API, but gives more control, and is easier to reason about. What is `postponeEndOperation`? I'm really worried about breaking the invariant that an operation wraps a dynamic scope.
marijnh commented 2013-08-14 15:48:11 +02:00 (Migrated from gitlab.com)

How about this:

Yes, that's fine too. See 46f44b8 and 800ff17.

What is postponeEndOperation?

Without it, when an addon handles a character and selectively returns CodeMirror.Pass to eachSelection, some of the changes to the document are made immediately (by the addon) and some later, inside readInput, which is strange visually and breaks undo. postponeEndOperation puts all changes in one operation. I agree it's scary, but I don't see a very clean solution except removing this functionality or making all the changes immediately, e.g. by parsing the onKeyPress event (I'm not sure that's possible, though). Perhaps it could be made more robust, though, by testing some edge cases (e.g. the handler throws, or readInput returns false for some reason).

> How about this: Yes, that's fine too. See 46f44b8 and 800ff17. > What is postponeEndOperation? Without it, when an addon handles a character and selectively returns CodeMirror.Pass to `eachSelection`, some of the changes to the document are made immediately (by the addon) and some later, inside `readInput`, which is strange visually and breaks `undo`. `postponeEndOperation` puts all changes in one operation. I agree it's scary, but I don't see a very clean solution except removing this functionality or making all the changes immediately, e.g. by parsing the onKeyPress event (I'm not sure that's possible, though). Perhaps it could be made more robust, though, by testing some edge cases (e.g. the handler throws, or `readInput` returns false for some reason).
marijnh commented 2013-08-14 15:48:48 +02:00 (Migrated from gitlab.com)

Fixed in 44f6291.

Fixed in 44f6291.
marijnh commented 2013-08-14 18:52:59 +02:00 (Migrated from gitlab.com)

Operations may nest (inner ones become part of the outer one) and
operations that don't change anything are very cheap, so I think you should
be able to simply wrap the whole thing in an operation. Or am I missing
something?
On Aug 14, 2013 3:49 PM, "Daniel Huigens" notifications@github.com wrote:

How about this:

Yes, that's fine too. See 46f44b8github.com/marijnh/CodeMirror@46f44b8and
800ff17 github.com/marijnh/CodeMirror@800ff17.

What is postponeEndOperation?

Without it, when an addon handles a character and selectively returns
CodeMirror.Pass to eachSelection, some of the changes to the document are
made immediately (by the addon) and some later, inside readInput, which
is strange visually and breaks undo. postponeEndOperation puts all
changes in one operation. I agree it's scary, but I don't see a very clean
solution except removing this functionality or making all the changes
immediately, e.g. by parsing the onKeyPress event (I'm not sure that's
possible, though). Perhaps it could be made more robust, though, by testing
some edge cases (e.g. the handler throws, or readInput returns false for
some reason).


Reply to this email directly or view it on GitHubhttps://github.com/marijnh/CodeMirror/pull/1656#issuecomment-22636392
.

Operations may nest (inner ones become part of the outer one) and operations that don't change anything are very cheap, so I think you should be able to simply wrap the whole thing in an operation. Or am I missing something? On Aug 14, 2013 3:49 PM, "Daniel Huigens" notifications@github.com wrote: > How about this: > > Yes, that's fine too. See 46f44b8https://github.com/marijnh/CodeMirror/commit/46f44b8and > 800ff17 https://github.com/marijnh/CodeMirror/commit/800ff17. > > What is postponeEndOperation? > > Without it, when an addon handles a character and selectively returns > CodeMirror.Pass to eachSelection, some of the changes to the document are > made immediately (by the addon) and some later, inside readInput, which > is strange visually and breaks undo. postponeEndOperation puts all > changes in one operation. I agree it's scary, but I don't see a very clean > solution except removing this functionality or making all the changes > immediately, e.g. by parsing the onKeyPress event (I'm not sure that's > possible, though). Perhaps it could be made more robust, though, by testing > some edge cases (e.g. the handler throws, or readInput returns false for > some reason). > > — > Reply to this email directly or view it on GitHubhttps://github.com/marijnh/CodeMirror/pull/1656#issuecomment-22636392 > .
marijnh commented 2013-08-16 11:55:13 +02:00 (Migrated from gitlab.com)

Well, that's a possibility but it wouldn't change much by itself since the operation should end after the timeout in fastPoll(), right?

Well, that's a possibility but it wouldn't change much by itself since the operation should end _after_ the timeout in `fastPoll()`, right?
marijnh commented 2013-08-16 13:09:59 +02:00 (Migrated from gitlab.com)

Could you write a bit more (possibly as a comment in the code) on how selecting and merging of selections is supposed to work? How does doc.unmergedSelections relate to doc.selections? What does doc.unmergedSel do?

We want to merge selections while still selecting to be able to draw them correctly, e.g. when you add a selection around a cursor, that cursor should disappear. (Also so that in the edge case that some text is typed while selecting something reasonable happens.)

However, when you move your mouse back to where you started, the merging should be undone. That's doc.unmergedSelections for, to be able to do that. doc.unmergedSel is the index of the primary selection in doc.unmergedSelections. We want to know that because that selection doesn't necessarily exist separately in doc.selections.

What exactly are the various ways in which multiple selections can occur?

Well, you can ctrl-click, ctrl-drag or call addSelection to add one selection, and (ctrl-)alt-drag or (but not on linux) (ctrl-)middle-drag to add a "block" selection, i.e. either selections around all text in the selected area, or a vertical line of cursors. In the current version of mergeSelections, selections can't touch, except if they both have text in them.

Why does merging the selections only have to look at adjacent ones?

Assuming the selections array is always sorted, all pairs or triples of selections that need to be merged will be "in a row", which is what mergeSelections looks for.

> Could you write a bit more (possibly as a comment in the code) on how selecting and merging of selections is supposed to work? How does doc.unmergedSelections relate to doc.selections? What does doc.unmergedSel do? We want to merge selections _while still selecting_ to be able to draw them correctly, e.g. when you add a selection around a cursor, that cursor should disappear. (Also so that in the edge case that some text is typed while selecting something reasonable happens.) However, when you move your mouse back to where you started, the merging should be undone. That's `doc.unmergedSelections` for, to be able to do that. `doc.unmergedSel` is the index of the primary selection in `doc.unmergedSelections`. We want to know that because that selection doesn't necessarily exist separately in `doc.selections`. > What exactly are the various ways in which multiple selections can occur? Well, you can ctrl-click, ctrl-drag or call addSelection to add one selection, and (ctrl-)alt-drag or (but not on linux) (ctrl-)middle-drag to add a "block" selection, i.e. either selections around all text in the selected area, or a vertical line of cursors. In the current version of `mergeSelections`, selections can't touch, except if they both have text in them. > Why does merging the selections only have to look at adjacent ones? Assuming the selections array is always sorted, all pairs or triples of selections that need to be merged will be "in a row", which is what `mergeSelections` looks for.
marijnh commented 2013-08-21 11:51:45 +02:00 (Migrated from gitlab.com)

I've pushed a version of your code, merged onto the current master branch, to the multi-sel branch of this repository. Let's work from that now. I did remove your changes to a few addons (active-line, matchbrackets, and matchtags) because the merging was messy and I felt those addons are actually saner if they just act on the main selection.

I'll start reviewing the code in detail and pushing patches when I encounter things I want to do differently. Please, if you do more work on this, work from my merged branch.

I've pushed a version of your code, merged onto the current master branch, to the `multi-sel` branch of this repository. Let's work from that now. I did remove your changes to a few addons (active-line, matchbrackets, and matchtags) because the merging was messy and I felt those addons are actually saner if they just act on the main selection. I'll start reviewing the code in detail and pushing patches when I encounter things I want to do differently. Please, if you do more work on this, work from my merged branch.
marijnh commented 2013-08-21 19:34:15 +02:00 (Migrated from gitlab.com)

Sure! Should I open a new pull request or should I rebase this branch to be the same as yours? Sorry, I'm not very familiar with git. In the matchtags addon, do you still want the jump-to-matching-tag command to work for all selections? It seems bad to just throw selections away.

Sure! Should I open a new pull request or should I rebase this branch to be the same as yours? Sorry, I'm not very familiar with git. In the matchtags addon, do you still want the jump-to-matching-tag command to work for all selections? It seems bad to just throw selections away.
marijnh commented 2013-08-21 20:21:56 +02:00 (Migrated from gitlab.com)

Rebasing this branch should work.

I'm having some second thoughts about the API I proposed, as well as about some decisions in your code, so maybe it's best if you just let me run with it for a bit, rather than doing conflicting work.

Rebasing this branch should work. I'm having some second thoughts about the API I proposed, as well as about some decisions in your code, so maybe it's best if you just let me run with it for a bit, rather than doing conflicting work.
marijnh commented 2013-08-22 10:47:34 +02:00 (Migrated from gitlab.com)

Alright, go ahead :)

Alright, go ahead :)
marijnh commented 2013-08-23 14:15:49 +02:00 (Migrated from gitlab.com)

Okay, I'm currently at " aaaaargh! withSelection is way too subtle, and doesn't work right, but I can't come up with an interface that actually does work conveniently and correctly ".

Will continue thinking about this, but as it stands I'm not sure how to proceed. A big problem is that you'll often want to somehow first compute a changeset from the set of selections, and then apply that atomically. This makes for much more awkward code, for example for indentSelection or killLine and various addons. But iterating over the selections and making changes will change the selections from underneath you, and cause hard-to-reason-about and unlikely-to-be-correct code.

Also, the way of adjusting the selection(s) for a change needs to be deeply rethought. The selAfter argument is completely broken with regards to multiple selections in its current form.

Okay, I'm currently at " _aaaaargh! `withSelection` is _way_ too subtle, and doesn't work right, but I can't come up with an interface that actually does work conveniently and correctly_ ". Will continue thinking about this, but as it stands I'm not sure how to proceed. A big problem is that you'll often want to somehow first compute a changeset from the set of selections, and then apply that atomically. This makes for _much_ more awkward code, for example for `indentSelection` or `killLine` and various addons. But iterating over the selections and making changes will change the selections from underneath you, and cause hard-to-reason-about and unlikely-to-be-correct code. Also, the way of adjusting the selection(s) for a change needs to be deeply rethought. The `selAfter` argument is completely broken with regards to multiple selections in its current form.
marijnh commented 2013-08-23 16:11:00 +02:00 (Migrated from gitlab.com)

Will continue thinking about this, but as it stands I'm not sure how to proceed. A big problem is that you'll often want to somehow first compute a changeset from the set of selections, and then apply that atomically. This makes for much more awkward code, for example for indentSelection or killLine and various addons. But iterating over the selections and making changes will change the selections from underneath you, and cause hard-to-reason-about and unlikely-to-be-correct code.

Well, what about this: commands are passed (cm, selections), and addons are responsible for iterating over the selections themselves? That's more flexible than what we have now and also more explicit.

> Will continue thinking about this, but as it stands I'm not sure how to proceed. A big problem is that you'll often want to somehow first compute a changeset from the set of selections, and then apply that atomically. This makes for much more awkward code, for example for indentSelection or killLine and various addons. But iterating over the selections and making changes will change the selections from underneath you, and cause hard-to-reason-about and unlikely-to-be-correct code. Well, what about this: commands are passed `(cm, selections)`, and addons are responsible for iterating over the selections themselves? That's more flexible than what we have now and also more explicit.
marijnh commented 2013-10-04 18:28:19 +02:00 (Migrated from gitlab.com)

Any more thoughts here?

Any more thoughts here?
marijnh commented 2013-10-04 21:33:51 +02:00 (Migrated from gitlab.com)

@ibdknox We're working with @marijnh to get this work completed in November.

`@ibdknox` We're working with `@marijnh` to get this work completed in November.
marijnh commented 2013-10-04 22:42:09 +02:00 (Migrated from gitlab.com)

Awesome. Thanks for the heads up. :)

Awesome. Thanks for the heads up. :)
marijnh commented 2013-10-25 20:42:18 +02:00 (Migrated from gitlab.com)

mentioned in issue #5690

mentioned in issue #5690
marijnh commented 2013-11-09 14:32:53 +01:00 (Migrated from gitlab.com)

mentioned in issue #483

mentioned in issue #483
marijnh commented 2013-11-13 22:53:21 +01:00 (Migrated from gitlab.com)

My implementation (tracked under #778) is now available. It's a full rewrite, since I wanted a fundamentally more robust model. Feedback welcome (I think I got all the features that are present in this code, except for the rounded corners on the selection, but I'm not entirely sure).

My implementation (tracked under #778) is now available. It's a full rewrite, since I wanted a fundamentally more robust model. Feedback welcome (I think I got all the features that are present in this code, except for the rounded corners on the selection, but I'm not entirely sure).
marijnh (Migrated from gitlab.com) closed this pull request 2013-11-13 22:53:21 +01:00
marijnh commented 2021-08-30 00:13:08 +02:00 (Migrated from gitlab.com)

mentioned in issue #4142

mentioned in issue #4142

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
codemirror/codemirror5!8814
No description provided.