Add Multi-caret support to TextEdit#61902
Conversation
3e84740 to
c211042
Compare
a7e00e8 to
be8ed21
Compare
|
Editor PR review meeting: Needs review/testing, really nice and wanted feature though |
d3f709e to
6f72a94
Compare
|
Thanks for making this! I haven't really used mutli-carets on any IDE and I don't have any stylistic prejudices. Also since it's an initial work, I'll not discuss usability. So here's just a good ol' dump of bugs instead:
|
|
Rebased and thanks for testing, hopefully fixed most of those issues. Regarding |
There was a problem hiding this comment.
| /* Make sure we don't try and remove empty space. */ | |
| // Make sure we don't try and remove empty space. |
There was a problem hiding this comment.
| /* This value informs us by how much we changed selection position by indenting right. */ | |
| /* Default is 1 for tab indentation. */ | |
| // This value informs us by how much we changed selection position by indenting right. | |
| // Default is 1 for tab indentation. |
This comment style is just used everywhere in the codebase.
Same below.
There was a problem hiding this comment.
| // Handle selection | |
| // Handle selection. |
Could as well fix the comments if they are being moved.
|
Ah right
-1 for all carets This should be mentioned somewhere in the docs, probably in the class description or |
There was a problem hiding this comment.
| // Check for overlaping carrets | |
| // Check for overlaping carrots |
jk
There was a problem hiding this comment.
Unnecessary comment.
...although I see there are similar comments below :/
It only repeats enum name.
There was a problem hiding this comment.
I think this comment is out of place now, no?
|
I checked through the code and it looks ok, I think. ~80% is just moving things around and adding caret index. Can't say much about the feature implementation, because it's rather complex, but I assume it's good, because it works xdd I left some comments, mostly some useless bikeshedding about comments. We use From more important stuff, there is this weird paste behavior I mentioned above. Although I used VS Code as reference, so not sure how it works in other editors. #61902 (comment) is rather important too, because the PR just adds I don't use multiple carets much, but I think a nice-to-have feature would be rectangular selection: |
|
Rider has the same behaviour as VS code, I think it is the expected behaviour. rider64_KWbv4gh2aZ.mp4 |
|
Pasting should now work like |
|
Thanks! |
|
It's an edge case, but might need some extra indication (I do not know what exactly will be appropriate here):
Screen.Recording.2022-10-06.at.10.04.07.mov |



This PR implements the initial support for multiple carets in
TextEdit. AdjustedCodeEditand the Editor to support them.This will most likely be the last major feature that I have time to make for 4.0.
Will look at adding multi-caret tests in a separate PR(s), as those will take a little while as this change is quite large.
For the initial support currently it's pretty bare bones, it's enabled by default. You can add an additional caret by
Alt + LeftClick. Typing will insert at all carets etc. Caret '0' is the main caret, the viewport will always follow this caret unless selecting via the mouse with a secondary caret. Each caret also has it's own selection.I've tried to make the behaviour when using multiple carets intuitive based on VSCode, SublimeText and QTCreator, but even between them there are a lot of differences and bugs. Not sure on performance with a lot of carets due to the checks etc but happy to optimise after.
In terms of API changes most of the viewport, caret and edit methods now take in a caret index, the caret index is one of the following:
-1for all carets0for the main caret1-xfor the restEach method is defaulted as appropriate so existing code should still work (as seen in current tests).
Each method will only accept
-1where it makes sense. For examplehas_selection(-1)will returntrueif any caret has a selection. Whereasget_caret_linedoes not have a strong use case for supporting-1, so is defaulted to0.Some utility methods have been exposed for handing multiple carets, they are as follows:
-
add_caret- Adds a caret.-
remove_caret- Removes a caret.-
get_caret_count- Gets the number of carets.-
remove_secondary_carets- Removes all additional carets.-
merge_overlapping_carets- Merges any carets and selections that are overlapping. Note this is not called when a caret changes position but after "actions", so if using the API it is possible to get into a state where carets do overlap.-
get_caret_index_edit_order- Returns a list of caret indexes in their edit order, this done from bottom to top. Edit order refers to the way action such asinsert_text_at_caretare applied.-
adjust_carets_after_edit- Should be called after editing a caret to reposition the carets affected by the edit such as adding a new line will need to push all carets and selection after that point down a line. This assumes edits are applied in edit order.Do due a typing now having the potential to consist of multiple edits, the undo redo system would put each caret as a separate step. Using
[begin|end]_complex_operationaround this would force it into a single step. However this would only make each letter when typing a step, the typing session would not be. We needed a way to have a "hanging" complex op, this comes in the form of an "action".Calling
start_actionwill end the current action if it is not the same, before starting the new one.end_actionmarks that the any changes from the action are over, however the action does not end until the timeout or another action is started. This can be done via directly callingstart_action,[begin|end]_complex_operationor via a edit that triggers and undo redo step outside of the[start|end]_actioncalls.There are currently four supported actions, intended as follow:
-
None- No current action.-
Typing- Normal typing-
Backspace- A backwards delete typically using the backspace-
Delete- A forwards delete typically using the delete key.This fixes some old issues where typing when the caret had a selection would be treated as an entire step, so completion would not work etc. Similarly, pressing the delete key would create an undo step for each letter, now they are treated like typing where multiple deletes are a single action.
Final notes on the undo redo, the system used to use a best guess approach to determine where the caret went, it will now restore the carets exactly as they were. One thing to watch out for, is many carets can spam the undo stack as it becomes difficult to merge them into a single item, as they move about due to other edits, this may cause the undo stack to fill up pretty fast.
Lastly, in order to get this is work, moved the viewport variables outside of the caret and gave them some slightly better names, and currently only the main caret is restored on launch.
closes godotengine/godot-proposals/issues/245
closes godotengine/godot-proposals/issues/3455