Java tutorial
/** * Copyright (c) 2015-present, Jim Kynde Meyer * All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.intellij.lang.jsgraphql.ide.project; import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.intellij.codeInsight.actions.ReformatCodeProcessor; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.execution.filters.UrlFilter; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.ui.ConsoleView; import com.intellij.icons.AllIcons; import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.projectView.impl.ProjectViewPane; import com.intellij.ide.util.PropertiesComponent; import com.intellij.json.JsonFileType; import com.intellij.lang.javascript.psi.JSFile; import com.intellij.lang.jsgraphql.JSGraphQLFileType; import com.intellij.lang.jsgraphql.JSGraphQLParserDefinition; import com.intellij.lang.jsgraphql.icons.JSGraphQLIcons; import com.intellij.lang.jsgraphql.ide.actions.JSGraphQLEditEndpointsAction; import com.intellij.lang.jsgraphql.ide.actions.JSGraphQLExecuteEditorAction; import com.intellij.lang.jsgraphql.ide.actions.JSGraphQLRestartLanguageServiceAction; import com.intellij.lang.jsgraphql.ide.actions.JSGraphQLToggleVariablesAction; import com.intellij.lang.jsgraphql.ide.configuration.JSGraphQLConfigurationListener; import com.intellij.lang.jsgraphql.ide.configuration.JSGraphQLConfigurationProvider; import com.intellij.lang.jsgraphql.ide.editor.JSGraphQLQueryContext; import com.intellij.lang.jsgraphql.ide.editor.JSGraphQLQueryContextHighlightVisitor; import com.intellij.lang.jsgraphql.ide.endpoints.JSGraphQLEndpoint; import com.intellij.lang.jsgraphql.ide.endpoints.JSGraphQLEndpointsModel; import com.intellij.lang.jsgraphql.ide.project.toolwindow.JSGraphQLErrorResult; import com.intellij.lang.jsgraphql.ide.project.toolwindow.JSGraphQLErrorTreeViewPanel; import com.intellij.lang.jsgraphql.ide.project.toolwindow.JSGraphQLLanguageToolWindowManager; import com.intellij.lang.jsgraphql.languageservice.JSGraphQLNodeLanguageServiceClient; import com.intellij.lang.jsgraphql.languageservice.JSGraphQLNodeLanguageServiceInstance; import com.intellij.lang.jsgraphql.psi.JSGraphQLFile; import com.intellij.lang.jsgraphql.schema.ide.project.JSGraphQLSchemaDirectoryNode; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; 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.ex.EditorEx; import com.intellij.openapi.editor.impl.EditorHeaderComponent; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; import com.intellij.testFramework.LightVirtualFile; import com.intellij.ui.EditorNotifications; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.SideBorder; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.content.Content; import com.intellij.ui.content.impl.ContentImpl; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ImmutableList; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.StopWatch; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Provides the project-specific GraphQL tool window, including errors view, console, and query result editor. */ public class JSGraphQLLanguageUIProjectService implements Disposable, FileEditorManagerListener, JSGraphQLConfigurationListener { public final static String GRAPH_QL_TOOL_WINDOW_NAME = "GraphQL"; public static final String GRAPH_QL_VARIABLES_JSON = "GraphQL.variables.json"; /** * Indicates that this virtual file backs a GraphQL variables editor */ public static final Key<Boolean> IS_GRAPH_QL_VARIABLES_VIRTUAL_FILE = Key.create(GRAPH_QL_VARIABLES_JSON); /** * Gets the variables editor associated with a .graphql query editor */ public static final Key<Editor> GRAPH_QL_VARIABLES_EDITOR = Key .create(GRAPH_QL_VARIABLES_JSON + ".variables.editor"); /** * Gets the query editor associated with a GraphQL variables editor */ public static final Key<Editor> GRAPH_QL_QUERY_EDITOR = Key.create(GRAPH_QL_VARIABLES_JSON + ".query.editor"); public final static Key<JSGraphQLEndpointsModel> JS_GRAPH_QL_ENDPOINTS_MODEL = Key .create("JSGraphQLEndpointsModel"); public final static Key<Boolean> JS_GRAPH_QL_EDITOR_QUERYING = Key.create("JSGraphQLEditorQuerying"); private static final String FILE_URL_PROPERTY = "fileUrl"; private final JSGraphQLLanguageToolWindowManager myToolWindowManager; private boolean myToolWindowManagerInitialized = false; @NotNull private final Project myProject; private final Map<String, ImmutableList<JSGraphQLErrorResult>> fileUriToErrors = Maps.newConcurrentMap(); private final Object myLock = new Object(); private FileEditor fileEditor; private JBLabel queryResultLabel; private JBLabel querySuccessLabel; public JSGraphQLLanguageUIProjectService(@NotNull final Project project) { myProject = project; final MessageBusConnection messageBusConnection = project.getMessageBus().connect(this); // the restart action final AnAction restartInstanceAction = ActionManager.getInstance() .getAction(JSGraphQLRestartLanguageServiceAction.class.getName()); // tool window myToolWindowManager = new JSGraphQLLanguageToolWindowManager(project, GRAPH_QL_TOOL_WINDOW_NAME, GRAPH_QL_TOOL_WINDOW_NAME, JSGraphQLIcons.UI.GraphQLNode, restartInstanceAction); Disposer.register(this, this.myToolWindowManager); // listen for editor file tab changes to update the list of current errors messageBusConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this); // add editor headers to already open files since we've only just added the listener for fileOpened() final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); for (VirtualFile virtualFile : fileEditorManager.getOpenFiles()) { insertEditorHeaderComponentIfApplicable(fileEditorManager, virtualFile); } // listen for configuration changes messageBusConnection.subscribe(JSGraphQLConfigurationListener.TOPIC, this); // finally init the tool window tabs initToolWindow(); // and notify to configure the schema project.putUserData(JSGraphQLParserDefinition.JSGRAPHQL_ACTIVATED, true); EditorNotifications.getInstance(project).updateAllNotifications(); // make sure the GraphQL schema is shown in the project tree if not already if (!JSGraphQLSchemaDirectoryNode.isShownForProject(project)) { final ProjectView projectView = ProjectView.getInstance(project); if (projectView != null && projectView.getCurrentProjectViewPane() instanceof ProjectViewPane) { projectView.refresh(); } } } public static JSGraphQLLanguageUIProjectService getService(@NotNull Project project) { return ServiceManager.getService(project, JSGraphQLLanguageUIProjectService.class); } public static void showConsole(@NotNull Project project) { showToolWindowContent(project, ConsoleView.class); } private static void showToolWindowContent(@NotNull Project project, @NotNull Class<?> contentClass) { UIUtil.invokeLaterIfNeeded(() -> { final ToolWindow toolWindow = ToolWindowManager.getInstance(project) .getToolWindow(GRAPH_QL_TOOL_WINDOW_NAME); if (toolWindow != null) { toolWindow.show(() -> { for (Content content : toolWindow.getContentManager().getContents()) { if (contentClass.isAssignableFrom(content.getComponent().getClass())) { toolWindow.getContentManager().setSelectedContent(content); break; } } }); } }); } public void logErrorsInCurrentFile(@NotNull PsiFile file, List<JSGraphQLErrorResult> errors) { final ImmutableList<JSGraphQLErrorResult> errorsList = ContainerUtil.immutableList(errors); fileUriToErrors.put(file.getVirtualFile().getUrl(), errorsList); UIUtil.invokeLaterIfNeeded(() -> { myToolWindowManager.logCurrentErrors(errorsList, false); }); } public Runnable connectToProcessHandler(OSProcessHandler processHandler) { UIUtil.invokeLaterIfNeeded(() -> { myToolWindowManager.connectToProcessHandler(processHandler); attachMessageFilter(); processHandler.addProcessListener(new ProcessAdapter() { public void onTextAvailable(ProcessEvent event, Key outputType) { if (StringUtils.isNotEmpty(event.getText())) { if (outputType == ProcessOutputTypes.STDERR) { // show the console on errors during initialization showConsole(myProject); } } } }); }); // callback for when the language service process has been fully initialized return () -> processHandler.addProcessListener(new ProcessAdapter() { public void onTextAvailable(ProcessEvent event, Key outputType) { if (StringUtils.isNotEmpty(event.getText())) { myProject.getMessageBus().syncPublisher(JSGraphQLLanguageServiceListener.TOPIC) .onProcessHandlerTextAvailable(event.getText()); } } }); } // ---- editor tabs listener ---- @Override public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { insertEditorHeaderComponentIfApplicable(source, file); } @Override public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { } @Override public void selectionChanged(@NotNull FileEditorManagerEvent event) { VirtualFile file = event.getNewFile(); if (file != null) { logErrorsForFile(file, false); } } // ---- configuration listener ---- @Override public void onEndpointsChanged(List<JSGraphQLEndpoint> endpoints) { reloadEndpoints(endpoints); } // ---- implementation ---- // -- endpoints -- private void reloadEndpoints(List<JSGraphQLEndpoint> newEndpoints) { final FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject); for (VirtualFile file : fileEditorManager.getOpenFiles()) { for (FileEditor editor : fileEditorManager.getEditors(file)) { if (editor instanceof TextEditor) { final JSGraphQLEndpointsModel endpointsModel = ((TextEditor) editor).getEditor() .getUserData(JS_GRAPH_QL_ENDPOINTS_MODEL); if (endpointsModel != null) { endpointsModel.reload(newEndpoints); } } } } } // -- editor header component -- private void insertEditorHeaderComponentIfApplicable(@NotNull FileEditorManager source, @NotNull VirtualFile file) { if (file.getFileType() == JSGraphQLFileType.INSTANCE || JSGraphQLFileType.isGraphQLScratchFile(source.getProject(), file)) { FileEditor fileEditor = source.getSelectedEditor(file); if (fileEditor instanceof TextEditor) { final Editor editor = ((TextEditor) fileEditor).getEditor(); if (editor.getHeaderComponent() instanceof JSGraphQLEditorHeaderComponent) { return; } UIUtil.invokeLaterIfNeeded(() -> { // ensure components are created on the swing thread final JComponent headerComponent = createHeaderComponent(fileEditor, editor); editor.setHeaderComponent(headerComponent); if (editor instanceof EditorEx) { ((EditorEx) editor).setPermanentHeaderComponent(headerComponent); } editor.getScrollingModel().scrollVertically(-1000); }); } } } private static class JSGraphQLEditorHeaderComponent extends EditorHeaderComponent { } private JComponent createHeaderComponent(FileEditor fileEditor, Editor editor) { final JSGraphQLEditorHeaderComponent headerComponent = new JSGraphQLEditorHeaderComponent(); // variables & settings actions final DefaultActionGroup settingsActions = new DefaultActionGroup(); settingsActions.add(new JSGraphQLEditEndpointsAction()); settingsActions.add(new JSGraphQLToggleVariablesAction()); final JComponent settingsToolbar = createToolbar(settingsActions); headerComponent.add(settingsToolbar, BorderLayout.WEST); // query execute final DefaultActionGroup queryActions = new DefaultActionGroup(); final AnAction executeGraphQLAction = ActionManager.getInstance() .getAction(JSGraphQLExecuteEditorAction.class.getName()); queryActions.add(executeGraphQLAction); final JComponent queryToolbar = createToolbar(queryActions); // configured endpoints combo box final List<JSGraphQLEndpoint> endpoints = JSGraphQLConfigurationProvider.getService(myProject) .getEndpoints(); final JSGraphQLEndpointsModel endpointsModel = new JSGraphQLEndpointsModel(endpoints, PropertiesComponent.getInstance(myProject)); final ComboBox endpointComboBox = new ComboBox(endpointsModel); endpointComboBox.setToolTipText("GraphQL endpoint"); editor.putUserData(JS_GRAPH_QL_ENDPOINTS_MODEL, endpointsModel); final JPanel endpointComboBoxPanel = new JPanel(new BorderLayout()); endpointComboBoxPanel.setBorder(BorderFactory.createEmptyBorder(1, 2, 2, 2)); endpointComboBoxPanel.add(endpointComboBox); // splitter to resize endpoints final OnePixelSplitter splitter = new OnePixelSplitter(false, .25F); splitter.setBorder(BorderFactory.createEmptyBorder()); splitter.setFirstComponent(endpointComboBoxPanel); splitter.setSecondComponent(queryToolbar); splitter.setHonorComponentsMinimumSize(true); splitter.setAndLoadSplitterProportionKey("JSGraphQLEndpointSplitterProportion"); splitter.setOpaque(false); splitter.getDivider().setOpaque(false); headerComponent.add(splitter, BorderLayout.CENTER); // variables editor final LightVirtualFile virtualFile = new LightVirtualFile(GRAPH_QL_VARIABLES_JSON, JsonFileType.INSTANCE, ""); final FileEditor variablesFileEditor = PsiAwareTextEditorProvider.getInstance().createEditor(myProject, virtualFile); final EditorEx variablesEditor = (EditorEx) ((TextEditor) variablesFileEditor).getEditor(); virtualFile.putUserData(IS_GRAPH_QL_VARIABLES_VIRTUAL_FILE, Boolean.TRUE); variablesEditor.setPlaceholder("{ variables }"); variablesEditor.setShowPlaceholderWhenFocused(true); variablesEditor.getSettings().setRightMarginShown(false); variablesEditor.getSettings().setAdditionalLinesCount(0); variablesEditor.getSettings().setShowIntentionBulb(false); variablesEditor.getSettings().setFoldingOutlineShown(false); variablesEditor.getSettings().setLineNumbersShown(false); variablesEditor.getSettings().setLineMarkerAreaShown(false); variablesEditor.getSettings().setCaretRowShown(false); variablesEditor.putUserData(JS_GRAPH_QL_ENDPOINTS_MODEL, endpointsModel); // hide variables by default variablesEditor.getComponent().setVisible(false); // link the query and variables editor together variablesEditor.putUserData(GRAPH_QL_QUERY_EDITOR, editor); editor.putUserData(GRAPH_QL_VARIABLES_EDITOR, variablesEditor); final NonOpaquePanel variablesPanel = new NonOpaquePanel(variablesFileEditor.getComponent()); variablesPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); Disposer.register(fileEditor, variablesFileEditor); headerComponent.add(variablesPanel, BorderLayout.SOUTH); return headerComponent; } private JComponent createToolbar(ActionGroup actionGroup) { final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, actionGroup, true); toolbar.setReservePlaceAutoPopupIcon(false); // don't want space after the last button final JComponent component = toolbar.getComponent(); component.setBorder(BorderFactory.createEmptyBorder()); return component; } public void executeGraphQL(Editor editor, VirtualFile virtualFile) { final JSGraphQLEndpointsModel endpointsModel = editor.getUserData(JS_GRAPH_QL_ENDPOINTS_MODEL); if (endpointsModel != null) { final JSGraphQLEndpoint selectedEndpoint = endpointsModel.getSelectedItem(); if (selectedEndpoint != null && selectedEndpoint.url != null) { final JSGraphQLQueryContext context = JSGraphQLQueryContextHighlightVisitor .getQueryContextBufferAndHighlightUnused(editor); final Map<String, Object> requestData = Maps.newLinkedHashMap(); requestData.put("query", context.query); try { requestData.put("variables", getQueryVariables(editor)); } catch (JsonSyntaxException jse) { if (myToolWindowManagerInitialized) { myToolWindowManager.logCurrentErrors(ContainerUtil.immutableList( new JSGraphQLErrorResult("Failed to parse variables as JSON: " + jse.getMessage(), virtualFile.getPath(), "Error", 0, 0)), true); } return; } final String requestJson = new Gson().toJson(requestData); final HttpClient httpClient = new HttpClient(new HttpClientParams()); try { final PostMethod method = new PostMethod(selectedEndpoint.url); setHeadersFromOptions(selectedEndpoint, method); method.setRequestEntity(new StringRequestEntity(requestJson, "application/json", "UTF-8")); ApplicationManager.getApplication().executeOnPooledThread(() -> { try { try { editor.putUserData(JS_GRAPH_QL_EDITOR_QUERYING, true); StopWatch sw = new StopWatch(); sw.start(); httpClient.executeMethod(method); final String responseJson = Optional.fromNullable(method.getResponseBodyAsString()) .or(""); sw.stop(); final Integer errorCount = getErrorCount(responseJson); if (fileEditor instanceof TextEditor) { final TextEditor textEditor = (TextEditor) fileEditor; UIUtil.invokeLaterIfNeeded(() -> { ApplicationManager.getApplication().runWriteAction(() -> { final Document document = textEditor.getEditor().getDocument(); document.setText(responseJson); if (requestJson.startsWith("{")) { final PsiFile psiFile = PsiDocumentManager.getInstance(myProject) .getPsiFile(document); if (psiFile != null) { new ReformatCodeProcessor(psiFile, false).run(); } } }); final StringBuilder queryResultText = new StringBuilder( virtualFile.getName()).append(": ").append(sw.getTime()) .append(" ms execution time, ") .append(bytesToDisplayString(responseJson.length())) .append(" response"); if (errorCount != null && errorCount > 0) { queryResultText.append(", ").append(errorCount).append(" error") .append(errorCount > 1 ? "s" : ""); if (context.onError != null) { context.onError.run(); } } queryResultLabel.setText(queryResultText.toString()); queryResultLabel.putClientProperty(FILE_URL_PROPERTY, virtualFile.getUrl()); if (!queryResultLabel.isVisible()) { queryResultLabel.setVisible(true); } querySuccessLabel.setVisible(errorCount != null); if (querySuccessLabel.isVisible()) { if (errorCount == 0) { querySuccessLabel .setBorder(BorderFactory.createEmptyBorder(2, 8, 0, 0)); querySuccessLabel.setIcon(AllIcons.General.InspectionsOK); } else { querySuccessLabel .setBorder(BorderFactory.createEmptyBorder(2, 12, 0, 4)); querySuccessLabel.setIcon(AllIcons.Ide.ErrorPoint); } } showToolWindowContent(myProject, fileEditor.getComponent().getClass()); textEditor.getEditor().getScrollingModel().scrollVertically(0); }); } } finally { editor.putUserData(JS_GRAPH_QL_EDITOR_QUERYING, null); } } catch (IOException e) { Notifications.Bus.notify( new Notification("GraphQL", "GraphQL Query Error", selectedEndpoint.url + ": " + e.getMessage(), NotificationType.WARNING), myProject); } }); } catch (UnsupportedEncodingException | IllegalStateException | IllegalArgumentException e) { Notifications.Bus.notify( new Notification("GraphQL", "GraphQL Query Error", selectedEndpoint.url + ": " + e.getMessage(), NotificationType.ERROR), myProject); } } } } private Integer getErrorCount(String responseJson) { try { final Map res = new Gson().fromJson(responseJson, Map.class); if (res != null) { final Object errors = res.get("errors"); if (errors instanceof Collection) { return ((Collection) errors).size(); } return 0; } } catch (JsonSyntaxException ignored) { } return null; } private Object getQueryVariables(Editor editor) { final Editor variablesEditor = editor.getUserData(GRAPH_QL_VARIABLES_EDITOR); if (variablesEditor != null) { final String variables = variablesEditor.getDocument().getText(); return new Gson().fromJson(variables, Map.class); } return null; } private static String bytesToDisplayString(long bytes) { if (bytes < 1000) return bytes + " bytes"; int exp = (int) (Math.log(bytes) / Math.log(1000)); String pre = ("kMGTPE").charAt(exp - 1) + ""; return String.format("%.1f %sb", bytes / Math.pow(1000, exp), pre); } private void setHeadersFromOptions(JSGraphQLEndpoint endpoint, PostMethod method) { if (endpoint.options != null) { final Object headers = endpoint.options.get("headers"); if (headers instanceof Map) { Map<String, Object> headersMap = (Map<String, Object>) headers; for (Map.Entry<String, Object> entry : headersMap.entrySet()) { method.setRequestHeader(entry.getKey(), String.valueOf(entry.getValue())); } } } } // -- instance management -- private void logErrorsForFile(VirtualFile file, boolean forceRefresh) { ImmutableList<JSGraphQLErrorResult> currentErrors = fileUriToErrors.get(file.getUrl()); if (currentErrors != null) { if (forceRefresh) { myToolWindowManager.logCurrentErrors(ContainerUtil.immutableList(Collections.emptyList()), false); } myToolWindowManager.logCurrentErrors(currentErrors, false); } else { // files we don't know the errors for if (myToolWindowManagerInitialized) { // don't attempt to log errors until the view is ready myToolWindowManager.logCurrentErrors(ContainerUtil.immutableList(Collections.emptyList()), false); } } } public void restartInstance() { synchronized (this.myLock) { final JSGraphQLNodeLanguageServiceInstance instance = JSGraphQLNodeLanguageServiceClient .getLanguageServiceInstance(myProject); if (instance != null) { if (myToolWindowManager != null) { myToolWindowManager.disconnectFromProcessHandler(); } instance.restart(() -> { final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor(); if (editor != null) { final VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); if (file != null) { final PsiFile psiFile = PsiUtilCore.getPsiFile(myProject, file); if (psiFile != null) { if (psiFile instanceof JSFile || psiFile instanceof JSGraphQLFile) { DaemonCodeAnalyzer.getInstance(myProject).restart(psiFile); } } logErrorsForFile(file, true); // force true to re-show the errors for the same file } } }); } } } private void attachMessageFilter() { final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject) .getToolWindow(GRAPH_QL_TOOL_WINDOW_NAME); if (toolWindow != null) { for (Content content : toolWindow.getContentManager().getContents()) { if (content.getComponent() instanceof ConsoleView) { ((ConsoleView) content.getComponent()).addMessageFilter(new UrlFilter()); } } } } private void createToolWindowResultEditor(ToolWindow toolWindow) { final LightVirtualFile virtualFile = new LightVirtualFile("GraphQL.result.json", JsonFileType.INSTANCE, ""); fileEditor = PsiAwareTextEditorProvider.getInstance().createEditor(myProject, virtualFile); if (fileEditor instanceof TextEditor) { final Editor editor = ((TextEditor) fileEditor).getEditor(); final EditorEx editorEx = (EditorEx) editor; // set read-only mode editorEx.setViewer(true); editorEx.getSettings().setShowIntentionBulb(false); editor.getSettings().setAdditionalLinesCount(0); editor.getSettings().setCaretRowShown(false); editor.getSettings().setBlinkCaret(false); // query result header final JSGraphQLEditorHeaderComponent header = new JSGraphQLEditorHeaderComponent(); querySuccessLabel = new JBLabel(); querySuccessLabel.setVisible(false); querySuccessLabel.setIconTextGap(0); header.add(querySuccessLabel, BorderLayout.WEST); queryResultLabel = new JBLabel("", null, SwingConstants.LEFT); queryResultLabel.setBorder(new EmptyBorder(4, 6, 4, 6)); queryResultLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); queryResultLabel.setVisible(false); queryResultLabel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { final String fileUrl = (String) queryResultLabel.getClientProperty(FILE_URL_PROPERTY); if (fileUrl != null) { final VirtualFile queryFile = VirtualFileManager.getInstance().findFileByUrl(fileUrl); if (queryFile != null) { final FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject); fileEditorManager.openFile(queryFile, true, true); } } } }); header.add(queryResultLabel, BorderLayout.CENTER); // finally set the header as permanent such that it's restored after searches editor.setHeaderComponent(header); editorEx.setPermanentHeaderComponent(header); } Disposer.register(this, fileEditor); final ContentImpl content = new ContentImpl(fileEditor.getComponent(), "Query result", true); content.setCloseable(false); toolWindow.getContentManager().addContent(content); } private void initToolWindow() { if (this.myToolWindowManager != null && !this.myProject.isDisposed()) { StartupManager.getInstance(this.myProject) .runWhenProjectIsInitialized(() -> ApplicationManager.getApplication().invokeLater(() -> { myToolWindowManager.init(); // we don't support project-level errors yet, so close any error tree view panels initially final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject) .getToolWindow(GRAPH_QL_TOOL_WINDOW_NAME); if (toolWindow != null) { for (Content content : toolWindow.getContentManager().getContents()) { if (content.isCloseable() && content.getComponent() instanceof JSGraphQLErrorTreeViewPanel) { toolWindow.getContentManager().removeContent(content, true); } } // show the current errors tab as empty myToolWindowManager .logCurrentErrors(ContainerUtil.immutableList(Collections.emptyList()), false); // don't want the console and the current errors to be closed for (Content content : toolWindow.getContentManager().getContents()) { content.setCloseable(false); } createToolWindowResultEditor(toolWindow); } myToolWindowManagerInitialized = true; }, myProject.getDisposed())); } } @Override public void dispose() { } }