Excellent!  How about setting the VirtualFile being displayed in a currently visible Editor (not necessarily selected editor), or replacing a specific Editor with a new one showing a specific VirtualFile?

0

This is not quite possible. Could you please explain the use-case/workflow for this "exchange of editor contents"?

0

Sure. For any given editor index > 0 (where there are more than 1 editors open), the user can select to "follow" the 1st editor.


- "1st editor" = top-leftmost editor.

- "Counterparts" = corresponding c/cpp header and source files. "example.h" and "example.cpp" are counterparts.

- "Following editor" = editor that automatically shows 1st editor's counterpart.

 

Consider these examples:


Example 1:

- There are two editors, Editor1 and Editor2.

- Editor2 is set to "follow" Editor1.

- Editor1 is showing FileA.h and Editor2 is showing FileA.cpp

- The user changes Editor1 to show FileB.h -> Editor2 automatically switches to showing FileB.cpp


Example 2:

- There are four editors, Editor1, 2, 3 and 4.

- Editor3 is set to "follow" Editor1.

- Editor1 is showing FileX.cpp and Editor3 is showing FileX.h

- The user changes Editor1 to show FileY.cpp -> Editor3 automatically switches to showing FileY.h

- Editor2 and 4 are left unchanged.

 


So the plugin needs to be able to say "replace arbitrary editor S with editor T [showing some new file]."

0

Not getting into the why or if there is a better way to achieve this.

I think this topic can help. It reloads editors for a different reason but does preserve the layout and pinned status of the editors so you can use it as a staring point: https://intellij-support.jetbrains.com/hc/en-us/community/posts/360001940480-How-to-close-reopen-an-editor-while-preserving-its-position-in-split-panes-?page=1#community_comment_360000286660

When you reload editors you can change the virtual file when opening the editor since a new editor is opened for the file in all cases.

0

Thanks for detailed samples. Vladimir's sample might indeed be a solution.

 

Another approach might be to provide your custom Editor component that simply holds the two related files in separate editors "together", so you'll have full control over synchronizing the two contents. See org.jetbrains.idea.devkit.testAssistant.TestDataGroupFileEditor in Plugin Devkit as sample.

0

I forgot to mention that EditorWindow.INITIAL_INDEX_KEYwent package private between 2016/10 and 2018/06 in the API.

I use a class which is instantiated once in the project component to wrap the key. Reflection will be used if getting it nicely does not work.

The code from above: file.putUserData(EditorWindow.INITIAL_INDEX_KEY, editorIndex)changes toINITIAL_INDEX_KEY.setEditorWindowInitialIndex(file, editorIndex).

class ProjectComponent {
    companion object {
        val INITIAL_INDEX_KEY: EditorWindowKey by lazy {
            EditorWindowKey()
        }
    }
}
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;
import java.lang.reflect.Field;

public class EditorWindowKey {
    @Nullable
    public final Key<Integer> myKey;

    public EditorWindowKey() {
        Key<Integer> key = null;
        try {
            key = EditorWindow.INITIAL_INDEX_KEY;
        } catch (IllegalAccessError e) {
            try {
                Field f = EditorWindow.class.getDeclaredField("INITIAL_INDEX_KEY"); //NoSuchFieldException
                f.setAccessible(true);
                //noinspection unchecked
                key = (Key<Integer>) f.get(null);
            } catch (IllegalAccessException | NoSuchFieldException ignored) {
                key = null;
            }
        }
        myKey = key;
    }

    public boolean haveKey() {
        return myKey != null;
    }

    public boolean setEditorWindowInitialIndex(@NotNull VirtualFile file, @Nullable Integer editorIndex) {
        if (myKey != null) {
            file.putUserData(myKey, editorIndex);
        }
        return myKey != null;
    }
}
0

Thanks for the info, looks very promising.

I'm having a Java Newbie issue. IntelliJ is not recognizing the 'companion' 'val' or 'lazy' keywords in:

class ProjectComponent {
companion object {
val INITIAL_INDEX_KEY: EditorWindowKey by lazy {
EditorWindowKey()
}
}
}

I've set the project and module SDK to OpenJDK 11 and language level 11.

0

That is Kotlin, not Java code.

0

Hah! Wow, they look similar.

0

That is because it is Kotlinnot Java.

Java equivalent but without the lazy initialization would be:

class ProjectComponent {
    final static EditorWindowKey INITIAL_INDEX_KEY = EditorWindowKey();
}
0

Yes, Kotlin runs on JVM and is Java compatible but so much more expressive.

I am converting everything that I can to Kotlin in my plugin.

0

Thanks for the info.

In my case this was actually pretty easy since I don't use tabs.  I just keep a list of open editors (one Editor per EditorWindow) and their positions on screen.  When an editor changes I compare the old order, new order and changed editor, figure out what editors I actually want and in what order I want them in, and then close all open editors (minus a dummy file) and re-open in the desired editors in the desired order.  Surprisingly it all happens pretty much instantaneously.

    private void setEditorFiles(ArrayList<VirtualFile> files, int selectIndex, Project project) {

//log.info("setEditorFiles(): " + files);

FileEditorManagerEx fileEditorManager = (FileEditorManagerEx) FileEditorManagerEx.getInstance(project);
EditorWindow[] windows = fileEditorManager.getWindows();

for (int i=0; i<windows.length; ++i) {
EditorWindow window = windows[i];

VirtualFile dummyFile = getFileNamed("dummy.txt", project);
fileEditorManager.openFileWithProviders(dummyFile, false, window);

EditorWithProviderComposite[] editors = window.getEditors();
for (EditorWithProviderComposite editor : editors) {
VirtualFile editorFile = editor.getFile();
if (editorFile != dummyFile) {
window.closeFile(editorFile, true, false);
}
}

VirtualFile file = files.get(i);
fileEditorManager.openFileWithProviders(file, false, window);

if (i == selectIndex) {

// save this file and wait for its editor to become visible,
// then get its content component and grab focus
nextFocusFile = file;
}

window.closeFile(dummyFile, true, false);
}
}

This brings me to my next question -- is it possible to force the Settings -> Editor -> General -> Editor Tabs -> Placement: None option from within my plugin?  I'm wiring the plugin for my own use, but if it can be reasonably easily made to play nice with an out-of-the-box CLion installation I might as well post it up on the Marketplace.

0

com.intellij.ide.ui.UISettings#setEditorTabPlacement using UISettings.TABS_NONE. But please make sure user confirms this choice before applying automatically.

0

Nice and simple.

Is there a standard way of presenting a confirmation modal dialog to the user?

0

com.intellij.openapi.ui.Messages#showYesNoDialog

0

Closing all editors and re-opening them is turning out to be quite snow in practice (outside my simple test project using small one-liner files) and causes an annoying flickering effect.

What would really be great is to be able to "intercept" the user's attempt to open a file, whether that action originates from them clicking an item in the Project tool window, or selecting a file from the Navigation Bar, or what-have-you.  The plugin could see that the user was trying to open file X, and decide what window to load it in.  Or perhaps equivalent would be the ability to "veto" when the IDE is trying to open a file in response to a user action.  Right now I'm using EditorFactoryListener.editorCreate() to figure out when the user opens a file, but at that point the Editor is already in the process or being displayed, and its destination EditorWindow has already been chosen.  Can these user actions be intercepted and/or canceled before an Editor is actually created?

0

While you could intercept some "known" ways of opening files, I doubt it will be possible to catch all ways completely - and any 3rd party plugin could add new ways of opening editors. There is no "earlier" way than using com.intellij.openapi.editor.event.EditorFactoryListener#editorCreated

0

请先登录再写评论。