[feature request] extending mouse handling to "remember" the column clicked when clicking past the end of line
Context
When moving vertically, cursors keeps track of their initial column. For instance in the following setup:
_____________s____
____i
____w________c__
A cursor starting on s moving down twice
The expected behavior is to i, then c.
Without this "column memory", it would end up at the position w.
Feature request
The goal of this feature request is to reuse this "column memory" in the context of a mouse click. For instance with the following setup:
____s X
____c________f____
With a mouse click on X, the cursor would visibly be set on s.
Moving down currently lead to c, the request is to go to f instead.
This makes keyboard adjustment after missing selecting long lines shorter.
Improving the usability of selection tools with low accuracy/precision (e.g. eye tracking).
Internally, the "column memory" works through the property leftoverVisibleColumns.
The formula seems to be initialColumn = cursor.column + cursor.leftoverVisibleColumns?
A distinction is made between 2 kind of cursor's position: "model" (actual text position) and "visual" (within the editor (seems to account for line wrapping, and injected text)).
From my limited testing, setting the visual position's leftoverVisibleColumns to mouseColumn - visualColumn is sufficient to handle the behavior.
Edge case: click under the textarea
There is an edge case when clicking under the last line:
The cursor gets move to the end of the text and mouseColumn - visualColumn ends up negative.
Negative values seems to snap the cursor back a few characters on the next vertical motion.
(input: click on 'X', then move up once):
____f__c_
_______s
X
(X: position of the click, s: visual position of the cursor, f: cursor after moving up with negative leftoverVisibleColumns, c: current behavior)
It's a strange behavior but somehow not too far from the keyboard's:
___s______e___
___1______2
Starting at s:
- 2 down then 1 up:
s,1,2,s - 3 down then 1 up:
s,1,2,2,e
Not sure if bug or feature, and even less if expending it to the mouse would make sens.
As for drilling the relevant values down to where there would need to be used, the relevant part of the stack trace seems to be:
- ViewController#dispatchMouse
- ViewController#moveTo
- BaseMoveToCommand#runCoreEditorCommand (inherited from CoreEditorCommand<MoveCommandOptions>)
- CursorMoveCommands#moveTo
The 1) is the last function having access to the mouseColumn, the 4) is the function setting the leftoverVisibleColumns to an hardcoded 0.
Also, ViewController has access to a IComputedEditorOption making it the ideal place for a feature switch.
If mouseColumn was to be drilled through the above call stack, there are 2 points that seems delicate to me:
adding mouseColumn to ViewController#moveTo
This is a public function of an exported class (could be used by 3rd party). I see 2 way the drilling could go:
- optional parameter on the function:
cursorColumn: number? = null - new private function just for the new behavior
Adding the parameter to the existing function would allow 3rd party's code with access to a ViewController to set arbitrary cursor offset.
adding a property to MoveCommandOptions to carry column info
- could be done to a new sub-interface to avoid polluting other actions
- but some other actions could make use of the cursorColumn
The commands using MoveCommandOptions (or derivated) are:
-
_moveTo,_moveToSelect,createCursor,_lastCursorMoveToSelect -
_wordSelect,_wordSelectDrag,lastCursorWordSelect -
_lineSelect,_lineSelectDrag,lastCursorLineSelect,lastCursorLineSelectDrag
If the change was extended to commands other than _moveTo, it would probably be _moveToSelect, createCursor, _lastCursorMoveToSelect.
So, to recap the main points left to address:
- How should a click below the code behave ? (negative
leftoverVisibleColumns? clamp to a positive/zero value ?) - Are there other interactions affected by the change that the feature breaks ?
- If the relevant argument have to be drilled down through the stack, does it happens through new private interface of by evolving existing one ?
- Are there other actions that could benefit from this capability ?