Smart brace completion results in incorrect caret data

Answered

I'm using a CaretListener to listen to changs on the document. Each change comes in as a CaretEvent and, for the most part, everything is fine.

However, I have a problem with closing braces. For example, when I type

public MyType()

At the point when I type the opening brace (but haven't typed the closing), my caret (as per CaretEvent) is here:

public MyType()caret

But on the screen, the caret is already here:

public MyType(caret)

As soon as I start typing, I get a caret event that moves it to the right place, but that event is too late. It only comes when I actually start typing, not before.

My question: how can I get the right caret position at a given point in time, taking into account smart brace closing? My code is as simple as

val position = caretEvent.caret?.offset ?: return
8 comments
Comment actions Permalink

To listen to actual changes in underlying document, one needs to use com.intellij.openapi.editor.event.DocumentListener instead of CaretListener (com.intellij.openapi.editor.event.DocumentEvent#getOffset). What feature are you building around this?

0
Comment actions Permalink

Here's what I'm trying to do: I'm recording every single change made to a document. To do this, I use the following piece of code:

val eventMulticaster = EditorFactory.getInstance().eventMulticaster

val caretListener = PluginCaretListener(snapshotManager)
val documentListener = PluginDocumentListener(snapshotManager)
val selectionListener = PluginSelectionListener(snapshotManager)

eventMulticaster.addSelectionListener(selectionListener)
eventMulticaster.addCaretListener(caretListener)
eventMulticaster.addDocumentListener(documentListener)

As you can see, I'm already using a DocumentListener. However, at the moment, I am implementing documentChanged() only to track changes to the document. In fact, I'm using DocumentEvent.getOffset() already, but I'm not using it like you are suggesting. Basically, my assumption is that

val caretPosition = startOffset + newText.length

And it works! I've got my whole system built around this assumption and it works perfectly. The only case where it fails is in brace overtype support.

0
Comment actions Permalink

Could you please post the full snippet of your DocumentListener implementation?

0
Comment actions Permalink

Sure, here it is

class PluginDocumentListener(val snapshotManager: ISnapshotManager) : DocumentListener
{
override fun documentChanged(event: DocumentEvent)
{
val document: Document = event.document
val fileKey = FileProcessorUtil.ProcessFile(document, snapshotManager) ?: return

val offset = event.offset
val newText = event.newFragment.toString()
val oldLength = event.oldLength

if (fileKey.first)
{
//println("[Skip first edit for new file]");
return
}

snapshotManager.newModification(fileKey.second, offset, oldLength, newText)
}
}
0
Comment actions Permalink

Just obtain actual caret offset after the document change, like you do in the caret listener:

caret.offset

I assume you are interested in primary caret's position. In this case caret instance can be obtained as

editor.caretModel.primaryCaret

 

0
Comment actions Permalink

Dmitry, it seems that DocumentEvent does not have an editor available. I can do event.document, but how do I get to the editor? This isn't a problem in SelectionEvent and CaretEvent, they expose an editor. But DocumentEvent does not.

0
Comment actions Permalink

That's because there can be multiple editors for the same document. If you are interested in all editors, you can get them via

EditorFactory.getInstance().getEditors(document)

If you are interested in a specific editor, you'll need to track it yourselves in some way.

1
Comment actions Permalink

Thank you! This worked, I now have good caret placement.

0

Please sign in to leave a comment.