package com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.params; import com.google.common.base.Throwables; import com.intellij.codeInsight.hint.HintManager; import com.intellij.icons.AllIcons; import com.intellij.json.JsonFileType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.EditorTabPresentationUtil; import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import com.neueda.jetbrains.plugin.graphdb.jetbrains.component.datasource.DataSourcesComponent; import com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.GraphConsoleView; import com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent; import com.neueda.jetbrains.plugin.graphdb.jetbrains.util.FileUtil; import com.neueda.jetbrains.plugin.graphdb.language.cypher.util.FileTypeExtensionUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; import static com.neueda.jetbrains.plugin.graphdb.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.*; public class ParametersPanel implements ParametersProvider { private static final FileDocumentManager FILE_DOCUMENT_MANAGER = FileDocumentManager.getInstance(); public static final Icon ICON_HELP = AllIcons.Actions.Help; private Editor globalParamEditor; private Editor fileSpecificParamEditor; private GraphConsoleView graphConsoleView; private MessageBus messageBus; private ParametersService service; private Project project; public void initialize(GraphConsoleView graphConsoleView, Project project) { this.graphConsoleView = graphConsoleView; this.messageBus = project.getMessageBus(); this.service = ServiceManager.getService(project, ParametersService.class); this.project = project; setupEditor(project); FileEditor selectedEditor = FileEditorManager.getInstance(project).getSelectedEditor(); if (selectedEditor != null && selectedEditor.getFile() != null && FileTypeExtensionUtil.isCypherFileTypeExtension(selectedEditor.getFile().getExtension())) { setupFileSpecificEditor(project, selectedEditor.getFile()); } } public String getGlobalParametersJson() { return globalParamEditor.getDocument().getText(); } public String getFileSpecificParametersJson() { return fileSpecificParamEditor != null ? fileSpecificParamEditor.getDocument().getText() : null; } private void initializeUi() { graphConsoleView.getGlobalParametersTab().add(globalParamEditor.getComponent(), BorderLayout.CENTER); service.registerParametersProvider(this); MessageBusConnection mbConnection = messageBus.connect(); mbConnection.subscribe(QueryParametersRetrievalErrorEvent.QUERY_PARAMETERS_RETRIEVAL_ERROR_EVENT_TOPIC, (exception, editor) -> { if (editor == null) { return; } String errorMessage; if (exception.getMessage() != null) { errorMessage = String.format("%s: %s", PARAMS_ERROR_COMMON_MSG, exception.getMessage()); } else { errorMessage = PARAMS_ERROR_COMMON_MSG; } HintManager.getInstance().showErrorHint(editor, errorMessage); }); mbConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() { // If file opened, fileOpenedSync->selectionChanged->fileOpened are called @Override public void selectionChanged(@NotNull FileEditorManagerEvent event) { releaseFileSpecificEditor(event.getOldFile()); VirtualFile newFile = event.getNewFile(); if (newFile != null && FileTypeExtensionUtil.isCypherFileTypeExtension(newFile.getExtension()) && project.getComponent(DataSourcesComponent.class).getDataSourceContainer().isDataSourceExists(getTabTitle(newFile))) { setupFileSpecificEditor(project, newFile); } } }); } private void setupEditor(Project project) { ApplicationManager.getApplication().runWriteAction(() -> { try { VirtualFile file = FileUtil.getScratchFile(project, "Neo4jGraphDbConsoleParametersPanel.json"); Document document = FILE_DOCUMENT_MANAGER.getDocument(file); globalParamEditor = createEditor(project, document); JLabel jLabel = new JLabel("<html>Global parameters:</html>"); jLabel.setIcon(ICON_HELP); jLabel.setToolTipText("Enter parameters in JSON format. Will be applied to any data source when executed"); globalParamEditor.setHeaderComponent(jLabel); setInitialContent(document); initializeUi(); } catch (Throwable e) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } }); } private void setupFileSpecificEditor(Project project, VirtualFile cypherFile) { if (project == null || cypherFile == null) { return; } try { String params = FileUtil.getParams(cypherFile); LightVirtualFile lightVirtualFile = new LightVirtualFile("", JsonFileType.INSTANCE, params); Document document = FileDocumentManager.getInstance().getDocument(lightVirtualFile); fileSpecificParamEditor = createEditor(project, document); VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { if (event.getFile().equals(cypherFile) && document != null) { FileUtil.setParams(cypherFile, document.getText()); } } }); JLabel jLabel = new JLabel("<html>Parameters for data source <b>" + getTabTitle(cypherFile) + "</b>:</html>"); jLabel.setIcon(ICON_HELP); jLabel.setToolTipText("Enter parameters in JSON format. Will be applied to <b>" + getTabTitle(cypherFile) + "</b> data source when executed"); fileSpecificParamEditor.setHeaderComponent(jLabel); if (document != null) { setInitialContent(document); } graphConsoleView.getFileSpecificParametersTab().add(fileSpecificParamEditor.getComponent(), BorderLayout.CENTER); } catch (Throwable e) { Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } } private void releaseFileSpecificEditor(VirtualFile oldFile) { if (fileSpecificParamEditor != null) { // set params in fileSpecificParamDocument.addDocumentListener() listener is a more universal but worse solution: // gets triggered immediately, debounce required FileUtil.setParams(oldFile, fileSpecificParamEditor.getDocument().getText()); graphConsoleView.getFileSpecificParametersTab().remove(fileSpecificParamEditor.getComponent()); if (!fileSpecificParamEditor.isDisposed()) { EditorFactory.getInstance().releaseEditor(fileSpecificParamEditor); } } fileSpecificParamEditor = null; } private String getTabTitle(VirtualFile file) { Window window = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow(); EditorWindow editorWindow = FileEditorManagerEx.getInstanceEx(project).getSplittersFor(window).getCurrentWindow(); return EditorTabPresentationUtil.getEditorTabTitle(project, file, editorWindow); } private void setInitialContent(Document document) { if (document != null && document.getText().isEmpty()) { final Runnable setTextRunner = () -> document.setText("{}"); ApplicationManager.getApplication() .invokeLater(() -> ApplicationManager.getApplication().runWriteAction(setTextRunner)); } } private static Editor createEditor(Project project, Document document) { return EditorFactory.getInstance().createEditor(document, project, JsonFileType.INSTANCE, false); } }