EditorEx not coloring JSON, language set, highlighter set

I have a working plugin with two panes, top one is a table and bottom one shows the JSON associated to the row you click in the table. This plugin is some niche stuff so I am not going to put in market place but the devs I work with use it a lot.

Here is the bottom panel code. Everything is working great other than the JSON “key”: “value” both have the same color as they are both string but if I open a JSON file in the IDE all the “key” have a different color which is what I want. Booleans and numbers show in the correct color. I would be nice if Rainbow Brackets worked in this view as well.

I don’t seem to be telling the editor the correct magic to fully realize this is JSON text. I have played around with a bunch of stuff including using PSI but finding help by Google searches tends to point me to IDE → Settings and now how to do it in a plugin. The JSON itself is properly formatted as I can drop it into the IDE as a file and it all paints correctly.

val editorFactory = EditorFactory.getInstance()
val document: Document = editorFactory.createDocument(getTheJson())
val editor = editorFactory.createEditor(document, project, JsonFileType.
INSTANCE
, true) as EditorEx?
editor?.
let 
{
    
val highlighter: EditorHighlighter = getInstance().createEditorHighlighter(
        project,
        JsonFileType.
INSTANCE
    
)
    
it
.setHighlighter(highlighter)
    
it
.
settings
.setLineNumbersShown(true)
    
it
.
settings
.setFoldingOutlineShown(true)
    splitPane.setSecondComponent(
it
.
component
)
}

We create editor in JsonPathEvaluateView#initJsonEditor this way:

  protected fun initJsonEditor(fileName: String, isViewer: Boolean, kind: EditorKind): Editor {
    val sourceVirtualFile = LightVirtualFile(fileName, JsonFileType.INSTANCE, "") // require strict JSON with quotes
    val sourceFile = PsiManager.getInstance(project).findFile(sourceVirtualFile)!!
    val document = PsiDocumentManager.getInstance(project).getDocument(sourceFile)!!

    val editor = EditorFactory.getInstance().createEditor(document, project, sourceVirtualFile, isViewer, kind)
    editor.settings.isLineNumbersShown = false
    return editor
  }

Slightly different results in that scrolling locks the parent key to top of window, which is nice, but the "key"of the JSON is still not in the correct color. It is in same color as all the “value” strings.

so “name”: “Kevin” should have name in one color and Kevin in a different color just like showing the JSON in a file in the IDE does it.

With this call

val editor = initJsonEditor("test.json", true, EditorKind.MAIN_EDITOR, showCallDetails(callList\[row\]))

fun initJsonEditor(fileName: String, isViewer: Boolean, kind: EditorKind, text: String): Editor {
    val sourceVirtualFile =
        LightVirtualFile(fileName, JsonFileType.INSTANCE, text) // require strict JSON with quotes
    val sourceFile = PsiManager.getInstance(project).findFile(sourceVirtualFile)!!
    val document = PsiDocumentManager.getInstance(project).getDocument(sourceFile)!!

    val editor = EditorFactory.getInstance().createEditor(document, project, sourceVirtualFile, isViewer, kind)
    editor.settings.isLineNumbersShown = true
    return editor
}

Looks like a rebuild fixed it. Your solution is working. Thanks

Looks like it ONLY painted it correct once. Without any changes to code, just AS restart, it is no longer painting the key correctly

The left side is just opening a JSON file from the project
The right side is what my viewing is displaying

You may install JSONPath plugin and check how it works. Compare to your implementation.

Action is called Evaluate JSONPath Expression

We use EditorKind.UNTYPED in this place, not MAIN_EDITOR, not sure if it is important though

I tried with the MAIN_EDITOR and it made no difference. What I am seeing is it will show the correct colors IF I look at a file diff. Unsure why it needs something else to kick it into gear. Maybe I need to force a repaint?

I am setting up the editor in table.selectionModel.addListSelectionListener I have tried to put the final calls to create the editor and set the second component to the EDT thread with it not working in same way when it was not a thread.

Editor barely will work inside tables, table in Swing has rather hacky paint. It is rather bad idea to put Editor there, as it is a heavy component with managed/Disposable resources. Please never forget to dispose Editor when you dispose/close UI.

The editor is not in the table, you tap on a table row to see the details in a Jcomponent held in a splitpane below the table. The calls I make to to update the jcomponent with a new editor happened during the table row clicked callback.

If I just leave the plugin running something will trigger it to properly paint / color code the JSON. I can’t find the exact pattern to make that happen or even why it happens.

Do you use revalidate and repaint on containers in Java Swing when you add/create new UI component dynamically?

When adding/removing components dynamically:

panel.add(new JButton("New"));
panel.revalidate();
panel.repaint();

Why both?

  1. revalidate() recalculates component positions and sizes.
  2. repaint() ensures the updated layout is actually painted immediately.

In practice, layout changes often trigger painting indirectly, but calling both is a common and safe pattern.

Been so long since I wrote any Swing code I forgot about these. They did not seem to help.

Here is my code, maybe you will spot something else I am doing wrong

    table.selectionModel.addListSelectionListener {
        if (!it.valueIsAdjusting) {
            val row = table.selectedRow
            if (row != -1 && row < callList.size) {
                val editor = initJsonEditor("test.json", true, EditorKind.MAIN_EDITOR, showCallDetails(callList\[row\]))
                splitPane.setSecondComponent(editor.component)
                splitPane.revalidate()
                splitPane.repaint()
            }
       }
    }
}

fun initJsonEditor(fileName: String, isViewer: Boolean, kind: EditorKind, text: String): Editor {
    val sourceVirtualFile =
        LightVirtualFile(fileName, JsonFileType.INSTANCE, text) // require strict JSON with quotes
    val sourceFile = PsiManager.getInstance(project).findFile(sourceVirtualFile)!!
    val document = PsiDocumentManager.getInstance(project).getDocument(sourceFile)!!
    val editor = EditorFactory.getInstance().createEditor(document, project, sourceVirtualFile, isViewer, kind)
    editor.settings.isLineNumbersShown = true
    return editor
}