Multiple Carets
Most editor actions (keyboard navigation, text insertion and deletion, etc.) will be applied to each caret independently. Each caret has its own associated selection, which is a continuous range of document characters (can be empty). When after some action two or more carets end up in the same visual position, they are merged into a single caret with their associated selections merged into a single one. A similar thing will happen when selections for several carets become overlapped: only one of the carets will remain, and the selections will be merged.
There's a concept of primary caret — the one on which non-multi-caret-aware actions and the actions which need a single-point document context (like code completion) will operate. Currently, the most recent caret is considered the primary one.
Core Functionality
Core logic related to multi-caret implementation such as accessing currently existing carets, adding and removing carets, is available via CaretModel
. For text selection, see SelectionModel
.
Methods in CaretModel
and SelectionModel
to query and modify caret and selection positions work by default on the primary caret. In the context of CaretModel.runForEachCaret()
method though, they operate on the current caret.
SelectionModel.getBlockSelectionStarts()
and getBlockSelectionEnds()
work in multi-caret state, returning all selected regions.
Editor Actions
EditorAction
and EditorActionHandler
When EditorActionHandler
is invoked, an additional parameter will be passed to it: a caret instance on which it should operate, or null
if it's invoked without any caret context. If the handler invokes another handler (delegate handler for the same actionId
or a completely unrelated handler), that parameter should normally be passed to the delegate unchanged (unless no context caret has been provided to the handler, but it needs to invoke another handler on a specific caret). Of course, the handler can just ignore the caret parameter if its functionality is not related to caret/selection position.
If the handler needs to implement multi-caret functionality it can do so explicitly in the overridden doExecute
method, but if it just needs that method to be invoked for each caret, it suffices to pass a parameter to EditorActionHandler
constructor to make doExecute
called for each caret when the handler is invoked without a specific caret context.
Editor Action Delegates
The following delegates are available:
There is no need to make any changes in the handlers to support multiple carets — they are already invoked for each caret.
Typing Actions
TypedActionHandler
, TypedHandlerDelegate
TypedActionHandler
and TypedHandlerDelegate
implementations are invoked only once for each typed character. If those handlers need to support multiple carets, they will need to implement that explicitly.
EditorModificationUtil
. typeInStringAtCaretHonorMultipleCarets()
method is available to do the most common task in this case: inserting the same text into all caret positions and/or moving all carets relatively to their current position. Examples of its usage:
Code Insight Actions
Existing actions inheriting from CodeInsightAction
will work for primary caret only. To support multiple carets, one should subclass MultiCaretCodeInsightAction
instead. Each caret might have a different editor and PSI instance, so using the old API is not possible.