How do I get the final caret offset after a document change event?

Answered

In my plugin I want to do something every time an editor's document changes, and I want to know what the caret offset will be after that change occurs. Easy right? I register a DocumentListener and measure the caretModel.offset inside it:

private inner class DocumentListener(val editor: Editor) : BulkAwareDocumentListener {

override fun documentChangedNonBulk(event: DocumentEvent) {
val caretOffset = editor.caretModel.offset
// call my function with the caret offset
editorManager.editorModified(editor, caretOffset)
}
}

Except, no. Turns out that if I type a character in an editor, the caretOffset inside documentChangedNonBulk is the offset before the inserted character, not after. Fine, what if I set a variable that the document has just changed and wait the CaretListener event instead?

private inner class DocumentListener(val editor: Editor) : BulkAwareDocumentListener {
override fun documentChangedNonBulk(event: DocumentEvent) {
this@EditorListener.documentChanged = true
}
}

private inner class EditorCaretListener : CaretListener {
override fun caretPositionChanged(event: CaretEvent) {
if (this@EditorListener.documentChanged) {
this@EditorListener.documentChanged = false
// call my function with the offset
     val caretOffset = editor.caretModel.offset
     editorManager.editorModified(editor, caretOffset)
return
}
}
}

 Except, the CaretListener doesn't fire if the caret didn't change due to induced events. As the documentation helpfully states:

Only explicit caret movements (caused by 'move' methods in Caret and CaretModel) are reported, 'induced' changes of caret offset due to document modifications are not reported.

So now I'm stuck. 

Other things I tried:

 - Use offset = event.offset + event.newLength inside documentChangedNonBulk. This doesn't work if the document change occurred after the caret (for example, if a parentheses was auto-closed and inserted after the caret).

 

0
5 comments

Sorry for delay. From DocumentListener, try obtaining editor(s) via com.intellij.openapi.editor.EditorFactory#editors(com.intellij.openapi.editor.Document, com.intellij.openapi.project.Project) and query their CaretModel instead, does it work now? What is the ultimate goal of knowing the caret position?

0

That seems to give me the same offset as

val caretOffset = editor.caretModel.offset

This is the offset before the change event, but I'd like the offset after the change event, if possible. 

The reason I want the offset is that I am building a code completion plugin which should make a suggestion as to how to complete the line of code that the user is typing. ideally a new completion should be shown on every document change. In order to properly know where to show the code completion, I need to know where the user's cursor is.

0

What kind of completion? com.intellij.codeInsight.completion.CompletionContributor extension point gives you all context information automatically.

0

It's an AI-powered code completion tool, so we'd like to provide multiline completions that the user can tab-accept. My understanding is that the CompletionContributor extension point is unsuitable for this, and that the recommended approach is to use Inlays instead. 

0

Try `com.intellij.codeInsight.editorActions.TypedHandlerDelegate#charTyped` 

0

Please sign in to leave a comment.