com.intellij.execution.console.LanguageConsoleImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.execution.console.LanguageConsoleImpl.java

Source

/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.execution.console;

import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.execution.impl.ConsoleViewUtil;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.ide.DataManager;
import com.intellij.ide.highlighter.HighlighterFactory;
import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.actions.EditorActionUtil;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.FocusChangeListener;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.JBColor;
import com.intellij.ui.SideBorder;
import com.intellij.util.*;
import com.intellij.util.ui.AbstractLayoutManager;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Gregory.Shrago
 * In case of REPL consider to use {@link LanguageConsoleBuilder}
 */
public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
    private static final int SEPARATOR_THICKNESS = 1;
    private final Project myProject;

    private final EditorEx myConsoleEditor;
    private final EditorEx myHistoryViewer;
    private final Document myEditorDocument;
    private final LightVirtualFile myVirtualFile;

    protected PsiFile myFile; // will change on language change

    private final JPanel myPanel = new JPanel(new MyLayout());
    private String myTitle;
    @Nullable
    private String myPrompt = "> ";
    private TextAttributes myPromptAttributes = ConsoleViewContentType.USER_INPUT.getAttributes();

    private final LightVirtualFile myHistoryFile;
    private Editor myCurrentEditor;

    private final AtomicBoolean myForceScrollToEnd = new AtomicBoolean(false);
    private final SingleAlarm myUpdateQueue;
    private Runnable myUiUpdateRunnable;

    private boolean myShowSeparatorLine = true;

    private final FocusChangeListener myFocusListener = new FocusChangeListener() {
        @Override
        public void focusGained(Editor editor) {
            myCurrentEditor = editor;
        }

        @Override
        public void focusLost(Editor editor) {
        }
    };

    public LanguageConsoleImpl(@NotNull Project project, @NotNull String title, @NotNull Language language) {
        this(project, title, language, true);
    }

    public LanguageConsoleImpl(@NotNull Project project, @NotNull String title, @NotNull Language language,
            boolean initComponents) {
        this(project, title, new LightVirtualFile(title, language, ""), initComponents);
    }

    public LanguageConsoleImpl(@NotNull Project project, @NotNull String title, @NotNull LightVirtualFile lightFile,
            boolean initComponents) {
        this(project, title, lightFile, initComponents, null);
    }

    LanguageConsoleImpl(@NotNull Project project, @NotNull String title, @NotNull LightVirtualFile lightFile,
            boolean initComponents, @Nullable PairFunction<VirtualFile, Project, PsiFile> psiFileFactory) {
        myProject = project;
        myTitle = title;
        myVirtualFile = lightFile;
        EditorFactory editorFactory = EditorFactory.getInstance();
        myHistoryFile = new LightVirtualFile(getTitle() + ".history.txt", PlainTextFileType.INSTANCE, "");
        myEditorDocument = FileDocumentManager.getInstance().getDocument(lightFile);
        assert myEditorDocument != null;
        myFile = psiFileFactory == null ? createFile(myVirtualFile, myEditorDocument, myProject)
                : psiFileFactory.fun(myVirtualFile, myProject);
        myConsoleEditor = (EditorEx) editorFactory.createEditor(myEditorDocument, myProject);
        myConsoleEditor.addFocusListener(myFocusListener);
        myCurrentEditor = myConsoleEditor;
        myHistoryViewer = (EditorEx) editorFactory
                .createViewer(((EditorFactoryImpl) editorFactory).createDocument(true), myProject);
        myUpdateQueue = new SingleAlarm(new Runnable() {
            @Override
            public void run() {
                if (isConsoleEditorEnabled()) {
                    myPanel.revalidate();
                    myPanel.repaint();
                }
                if (myUiUpdateRunnable != null) {
                    myUiUpdateRunnable.run();
                }
            }
        }, 300, this);

        // action shortcuts are not yet registered
        ApplicationManager.getApplication().invokeLater(new Runnable() {
            @Override
            public void run() {
                installEditorFactoryListener();
            }
        }, myProject.getDisposed());

        if (initComponents) {
            initComponents();
        }
    }

    public void initComponents() {
        setupComponents();

        myPanel.add(myHistoryViewer.getComponent());
        myPanel.add(myConsoleEditor.getComponent());

        DataManager.registerDataProvider(myPanel, new TypeSafeDataProviderAdapter(this));

        myHistoryViewer.getComponent().addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                if (myForceScrollToEnd.compareAndSet(true, false)) {
                    scrollHistoryToEnd();
                }
            }

            @Override
            public void componentShown(ComponentEvent e) {
                componentResized(e);
            }
        });
        setPromptInner(myPrompt);
    }

    public void setConsoleEditorEnabled(boolean consoleEditorEnabled) {
        if (isConsoleEditorEnabled() == consoleEditorEnabled) {
            return;
        }

        if (consoleEditorEnabled) {
            FileEditorManager.getInstance(getProject()).closeFile(myVirtualFile);
            myPanel.removeAll();
            myPanel.add(myHistoryViewer.getComponent());
            myPanel.add(myConsoleEditor.getComponent());

            myHistoryViewer.setHorizontalScrollbarVisible(false);
            myCurrentEditor = myConsoleEditor;
        } else {
            myPanel.removeAll();
            myPanel.add(myHistoryViewer.getComponent(), BorderLayout.CENTER);
            myHistoryViewer.setHorizontalScrollbarVisible(true);
        }
    }

    public void setShowSeparatorLine(boolean showSeparatorLine) {
        myShowSeparatorLine = showSeparatorLine;
    }

    private void setupComponents() {
        setupEditorDefault(myConsoleEditor);
        setupEditorDefault(myHistoryViewer);

        //noinspection ConstantConditions
        if (SEPARATOR_THICKNESS > 0 && myShowSeparatorLine) {
            myHistoryViewer.getComponent().setBorder(new SideBorder(JBColor.LIGHT_GRAY, SideBorder.BOTTOM));
        }
        myHistoryViewer.getComponent().setMinimumSize(new Dimension(0, 0));
        myHistoryViewer.getComponent().setPreferredSize(new Dimension(0, 0));
        myHistoryViewer.setCaretEnabled(false);

        myConsoleEditor.setHorizontalScrollbarVisible(true);
        myConsoleEditor.addEditorMouseListener(
                EditorActionUtil.createEditorPopupHandler(IdeActions.GROUP_CONSOLE_EDITOR_POPUP));
        myConsoleEditor.setHighlighter(EditorHighlighterFactory.getInstance().createEditorHighlighter(myVirtualFile,
                myConsoleEditor.getColorsScheme(), myProject));

        myConsoleEditor.getScrollingModel().addVisibleAreaListener(new VisibleAreaListener() {
            @Override
            public void visibleAreaChanged(VisibleAreaEvent e) {
                final int offset = myConsoleEditor.getScrollingModel().getHorizontalScrollOffset();
                final ScrollingModel model = myHistoryViewer.getScrollingModel();
                final int historyOffset = model.getHorizontalScrollOffset();
                if (historyOffset != offset) {
                    try {
                        model.disableAnimation();
                        model.scrollHorizontally(offset);
                    } finally {
                        model.enableAnimation();
                    }
                }
            }
        });
        final DocumentAdapter docListener = new DocumentAdapter() {
            @Override
            public void documentChanged(final DocumentEvent e) {
                queueUiUpdate(false);
            }
        };
        myEditorDocument.addDocumentListener(docListener, this);
        myHistoryViewer.getDocument().addDocumentListener(docListener, this);

        myHistoryViewer.getContentComponent().addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent event) {
                if (isConsoleEditorEnabled() && UIUtil.isReallyTypedEvent(event)) {
                    myConsoleEditor.getContentComponent().requestFocus();
                    myConsoleEditor.processKeyTyped(event);
                }
            }
        });

        //noinspection deprecation
        for (AnAction action : createActions()) {
            action.registerCustomShortcutSet(action.getShortcutSet(), myConsoleEditor.getComponent());
        }
        EmptyAction.registerActionShortcuts(myHistoryViewer.getComponent(), myConsoleEditor.getComponent());
    }

    public boolean isConsoleEditorEnabled() {
        return myPanel.getComponentCount() > 1;
    }

    @NotNull
    @Deprecated
    /**
     * @deprecated LanguageConsoleImpl is not intended to be extended
     */
    protected AnAction[] createActions() {
        return AnAction.EMPTY_ARRAY;
    }

    @SuppressWarnings("UnusedDeclaration")
    @Deprecated
    /**
     * @deprecated Use {@link #setInputText}
     * to remove in IDEA 15
     */
    public void setTextToEditor(@NotNull String text) {
        setInputText(text);
    }

    protected void setupEditorDefault(@NotNull EditorEx editor) {
        ConsoleViewUtil.setupConsoleEditor(editor, false, false);
        editor.getContentComponent().setFocusCycleRoot(false);
        editor.setHorizontalScrollbarVisible(false);
        editor.setVerticalScrollbarVisible(true);
        editor.setBorder(null);

        final EditorSettings editorSettings = editor.getSettings();
        if (myHistoryViewer != editor) {
            editorSettings.setAdditionalLinesCount(1);
        }
        editorSettings.setAdditionalColumnsCount(1);
    }

    public void setUiUpdateRunnable(Runnable uiUpdateRunnable) {
        assert myUiUpdateRunnable == null : "can be set only once";
        myUiUpdateRunnable = uiUpdateRunnable;
    }

    public void flushAllUiUpdates() {
        myUpdateQueue.flush();
    }

    @SuppressWarnings("UnusedDeclaration")
    @NotNull
    @Deprecated
    public LightVirtualFile getHistoryFile() {
        return myHistoryFile;
    }

    @Nullable
    public String getPrompt() {
        return myPrompt;
    }

    public void setPromptAttributes(@NotNull TextAttributes textAttributes) {
        myPromptAttributes = textAttributes;
    }

    public void setPrompt(@Nullable String prompt) {
        // always add space to the prompt otherwise it may look ugly
        myPrompt = prompt != null && !prompt.endsWith(" ") ? prompt + " " : prompt;
        setPromptInner(myPrompt);
    }

    private void setPromptInner(@Nullable final String prompt) {
        myUpdateQueue.checkDisposed();

        UIUtil.invokeAndWaitIfNeeded(new Runnable() {
            @Override
            public void run() {
                myConsoleEditor.setPrefixTextAndAttributes(prompt, myPromptAttributes);
                if (myPanel.isVisible()) {
                    queueUiUpdate(false);
                }
            }
        });
    }

    public void setEditable(boolean editable) {
        myConsoleEditor.setRendererMode(!editable);
        setPromptInner(editable ? myPrompt : "");
    }

    public boolean isEditable() {
        return !myConsoleEditor.isRendererMode();
    }

    @NotNull
    public PsiFile getFile() {
        return myFile;
    }

    @NotNull
    public VirtualFile getVirtualFile() {
        return myVirtualFile;
    }

    @NotNull
    public EditorEx getHistoryViewer() {
        return myHistoryViewer;
    }

    @NotNull
    public Document getEditorDocument() {
        return myEditorDocument;
    }

    @NotNull
    public EditorEx getConsoleEditor() {
        return myConsoleEditor;
    }

    @NotNull
    public Project getProject() {
        return myProject;
    }

    @NotNull
    public String getTitle() {
        return myTitle;
    }

    public void setTitle(@NotNull String title) {
        myTitle = title;
    }

    public void printToHistory(@NotNull CharSequence text, @NotNull TextAttributes attributes) {
        ApplicationManager.getApplication().assertIsDispatchThread();
        text = StringUtilRt.unifyLineSeparators(text);
        final boolean scrollToEnd = shouldScrollHistoryToEnd();
        addTextToHistory(text, attributes);
        if (scrollToEnd) {
            scrollHistoryToEnd();
        }
        queueUiUpdate(scrollToEnd);
    }

    protected void addTextToHistory(@Nullable CharSequence text, @Nullable TextAttributes attributes) {
        if (StringUtil.isEmpty(text) || attributes == null) {
            return;
        }

        Document history = myHistoryViewer.getDocument();
        int offset = appendToHistoryDocument(history, text);
        DocumentMarkupModel.forDocument(history, myProject, true).addRangeHighlighter(offset,
                offset + text.length(), HighlighterLayer.SYNTAX, attributes, HighlighterTargetArea.EXACT_RANGE);
    }

    @SuppressWarnings("UnusedDeclaration")
    @Deprecated
    /**
     * @deprecated Use {@link LanguageConsoleBuilder},
     * {@link LanguageConsoleBuilder#registerExecuteAction)} or
     * {@link ConsoleExecuteAction#prepareRunExecuteAction)}
     *
     * to remove in IDEA 15
     */
    public String addCurrentToHistory(@NotNull TextRange textRange, boolean erase, boolean preserveMarkup) {
        return addToHistoryInner(textRange, myConsoleEditor, erase, preserveMarkup);
    }

    public String addToHistory(@NotNull TextRange textRange, @NotNull EditorEx editor, boolean preserveMarkup) {
        return addToHistoryInner(textRange, editor, false, preserveMarkup);
    }

    @NotNull
    public String prepareExecuteAction(boolean addToHistory, boolean preserveMarkup, boolean clearInput) {
        Editor editor = getCurrentEditor();
        Document document = editor.getDocument();
        String text = document.getText();
        TextRange range = new TextRange(0, document.getTextLength());
        if (!clearInput) {
            editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
        }

        if (addToHistory) {
            addToHistoryInner(range, myConsoleEditor, clearInput, preserveMarkup);
        } else if (clearInput) {
            setInputText("");
        }
        return text;
    }

    @NotNull
    protected String addToHistoryInner(@NotNull final TextRange textRange, @NotNull final EditorEx editor,
            boolean erase, final boolean preserveMarkup) {
        ApplicationManager.getApplication().assertIsDispatchThread();

        String result = addTextRangeToHistory(textRange, editor, preserveMarkup);
        if (erase) {
            DocumentUtil.writeInRunUndoTransparentAction(new Runnable() {
                @Override
                public void run() {
                    editor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset());
                }
            });
        }
        // always scroll to end on user input
        scrollHistoryToEnd();
        queueUiUpdate(true);
        return result;
    }

    public boolean shouldScrollHistoryToEnd() {
        final Rectangle visibleArea = myHistoryViewer.getScrollingModel().getVisibleArea();
        final Dimension contentSize = myHistoryViewer.getContentSize();
        return contentSize.getHeight()
                - visibleArea.getMaxY() < (getMinHistoryLineCount() * myHistoryViewer.getLineHeight());
    }

    private void scrollHistoryToEnd() {
        if (myHistoryViewer.getDocument().getTextLength() != 0) {
            EditorUtil.scrollToTheEnd(myHistoryViewer);
        }
    }

    @NotNull
    protected String addTextRangeToHistory(@NotNull TextRange textRange, @NotNull EditorEx inputEditor,
            boolean preserveMarkup) {
        doAddPromptToHistory();

        final Document history = myHistoryViewer.getDocument();
        final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
        final int localStartOffset = textRange.getStartOffset();
        String text;
        EditorHighlighter highlighter;
        if (inputEditor instanceof EditorWindow) {
            PsiFile file = ((EditorWindow) inputEditor).getInjectedFile();
            highlighter = HighlighterFactory.createHighlighter(file.getVirtualFile(),
                    EditorColorsManager.getInstance().getGlobalScheme(), getProject());
            String fullText = InjectedLanguageUtil.getUnescapedText(file, null, null);
            highlighter.setText(fullText);
            text = textRange.substring(fullText);
        } else {
            text = inputEditor.getDocument().getText(textRange);
            highlighter = inputEditor.getHighlighter();
        }
        //offset can be changed after text trimming after insert due to buffer constraints
        int offset = appendToHistoryDocument(history, text);

        final HighlighterIterator iterator = highlighter.createIterator(localStartOffset);
        final int localEndOffset = textRange.getEndOffset();
        while (!iterator.atEnd()) {
            final int itStart = iterator.getStart();
            if (itStart > localEndOffset) {
                break;
            }
            final int itEnd = iterator.getEnd();
            if (itEnd >= localStartOffset) {
                final int start = Math.max(itStart, localStartOffset) - localStartOffset + offset;
                final int end = Math.min(itEnd, localEndOffset) - localStartOffset + offset;
                markupModel.addRangeHighlighter(start, end, HighlighterLayer.SYNTAX, iterator.getTextAttributes(),
                        HighlighterTargetArea.EXACT_RANGE);
            }
            iterator.advance();
        }
        if (preserveMarkup) {
            duplicateHighlighters(markupModel,
                    DocumentMarkupModel.forDocument(inputEditor.getDocument(), myProject, true), offset, textRange);
            // don't copy editor markup model, i.e. brace matcher, spell checker, etc.
            // duplicateHighlighters(markupModel, inputEditor.getMarkupModel(), offset, textRange);
        }
        if (!text.endsWith("\n")) {
            appendToHistoryDocument(history, "\n");
        }
        return text;
    }

    protected void doAddPromptToHistory() {
        addTextToHistory(myPrompt, myPromptAttributes);
    }

    // returns the real (cyclic-buffer-aware) start offset of the inserted text
    protected int appendToHistoryDocument(@NotNull Document history, @NotNull CharSequence text) {
        ApplicationManager.getApplication().assertIsDispatchThread();
        history.insertString(history.getTextLength(), text);
        return history.getTextLength() - text.length();
    }

    private static void duplicateHighlighters(@NotNull MarkupModel to, @NotNull MarkupModel from, int offset,
            @NotNull TextRange textRange) {
        for (RangeHighlighter rangeHighlighter : from.getAllHighlighters()) {
            if (!rangeHighlighter.isValid()) {
                continue;
            }
            Object tooltip = rangeHighlighter.getErrorStripeTooltip();
            HighlightInfo highlightInfo = tooltip instanceof HighlightInfo ? (HighlightInfo) tooltip : null;
            if (highlightInfo != null) {
                if (highlightInfo.getSeverity() != HighlightSeverity.INFORMATION) {
                    continue;
                }
                if (highlightInfo.type.getAttributesKey() == EditorColors.IDENTIFIER_UNDER_CARET_ATTRIBUTES) {
                    continue;
                }
            }
            final int localOffset = textRange.getStartOffset();
            final int start = Math.max(rangeHighlighter.getStartOffset(), localOffset) - localOffset;
            final int end = Math.min(rangeHighlighter.getEndOffset(), textRange.getEndOffset()) - localOffset;
            if (start > end) {
                continue;
            }
            final RangeHighlighter h = to.addRangeHighlighter(start + offset, end + offset,
                    rangeHighlighter.getLayer(), rangeHighlighter.getTextAttributes(),
                    rangeHighlighter.getTargetArea());
            ((RangeHighlighterEx) h).setAfterEndOfLine(((RangeHighlighterEx) rangeHighlighter).isAfterEndOfLine());
        }
    }

    @NotNull
    public JComponent getComponent() {
        return myPanel;
    }

    public void queueUiUpdate(boolean forceScrollToEnd) {
        myForceScrollToEnd.compareAndSet(false, forceScrollToEnd);
        if (myUpdateQueue.isDisposed()) {
            return;
        }
        myUpdateQueue.request();
    }

    @Override
    public void dispose() {
        EditorFactory editorFactory = EditorFactory.getInstance();
        editorFactory.releaseEditor(myConsoleEditor);
        editorFactory.releaseEditor(myHistoryViewer);

        if (getProject().isOpen()) {
            FileEditorManager editorManager = FileEditorManager.getInstance(getProject());
            if (editorManager.isFileOpen(myVirtualFile)) {
                editorManager.closeFile(myVirtualFile);
            }
        }
    }

    @Override
    public void calcData(@NotNull DataKey key, @NotNull DataSink sink) {
        if (OpenFileDescriptor.NAVIGATE_IN_EDITOR == key) {
            sink.put(OpenFileDescriptor.NAVIGATE_IN_EDITOR, myConsoleEditor);
        } else if (getProject().isInitialized()) {
            sink.put(key, FileEditorManagerEx.getInstanceEx(getProject()).getData(key.getName(), myConsoleEditor,
                    myConsoleEditor.getCaretModel().getCurrentCaret()));
        }
    }

    private void installEditorFactoryListener() {
        FileEditorManagerAdapter fileEditorListener = new FileEditorManagerAdapter() {
            @Override
            public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
                if (myConsoleEditor == null || !Comparing.equal(file, myVirtualFile)) {
                    return;
                }

                Editor selectedTextEditor = source.getSelectedTextEditor();
                for (FileEditor fileEditor : source.getAllEditors(file)) {
                    if (!(fileEditor instanceof TextEditor)) {
                        continue;
                    }

                    final EditorEx editor = (EditorEx) ((TextEditor) fileEditor).getEditor();
                    editor.addFocusListener(myFocusListener);
                    if (selectedTextEditor == editor) { // already focused
                        myCurrentEditor = editor;
                    }
                    EmptyAction.registerActionShortcuts(editor.getComponent(), myConsoleEditor.getComponent());
                    editor.getCaretModel().addCaretListener(new CaretAdapter() {
                        @Override
                        public void caretPositionChanged(CaretEvent e) {
                            queueUiUpdate(false);
                        }
                    });
                }
                queueUiUpdate(false);
            }

            @Override
            public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
                if (!Comparing.equal(file, myVirtualFile)) {
                    return;
                }
                if (myUiUpdateRunnable != null
                        && !Boolean.TRUE.equals(file.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) {
                    if (myCurrentEditor != null && myCurrentEditor.isDisposed()) {
                        myCurrentEditor = null;
                    }
                    ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable);
                }
            }
        };
        myProject.getMessageBus().connect(this).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER,
                fileEditorListener);
        FileEditorManager editorManager = FileEditorManager.getInstance(getProject());
        if (editorManager.isFileOpen(myVirtualFile)) {
            fileEditorListener.fileOpened(editorManager, myVirtualFile);
        }
    }

    public Editor getCurrentEditor() {
        return ObjectUtils.chooseNotNull(myCurrentEditor, myConsoleEditor);
    }

    public Language getLanguage() {
        return myVirtualFile.getLanguage();
    }

    public void setLanguage(@NotNull Language language) {
        myVirtualFile.setLanguage(language);
        myVirtualFile.setContent(myEditorDocument, myEditorDocument.getText(), false);
        FileContentUtil.reparseFiles(myProject, Collections.<VirtualFile>singletonList(myVirtualFile), false);
        myFile = createFile(myVirtualFile, myEditorDocument, myProject);
    }

    public void setInputText(@NotNull final String query) {
        DocumentUtil.writeInRunUndoTransparentAction(new Runnable() {
            @Override
            public void run() {
                myConsoleEditor.getDocument().setText(query);
            }
        });
    }

    @NotNull
    protected PsiFile createFile(@NotNull LightVirtualFile virtualFile, @NotNull Document document,
            @NotNull Project project) {
        return ObjectUtils.assertNotNull(PsiManager.getInstance(project).findFile(virtualFile));
    }

    boolean isHistoryViewerForceAdditionalColumnsUsage() {
        return true;
    }

    int getMinHistoryLineCount() {
        return 2;
    }

    private class MyLayout extends AbstractLayoutManager {
        @Override
        public Dimension preferredLayoutSize(final Container parent) {
            return new Dimension(0, 0);
        }

        @Override
        public void layoutContainer(@NotNull final Container parent) {
            final int componentCount = parent.getComponentCount();
            if (componentCount == 0) {
                return;
            }

            final EditorEx history = myHistoryViewer;
            final EditorEx input = componentCount == 2 ? myConsoleEditor : null;
            if (input == null) {
                parent.getComponent(0).setBounds(parent.getBounds());
                return;
            }

            final Dimension panelSize = parent.getSize();
            if (panelSize.getHeight() <= 0) {
                return;
            }
            final Dimension historySize = history.getContentSize();
            final Dimension inputSize = input.getContentSize();

            int newInputHeight;
            // deal with width
            final int width = Math.max(inputSize.width, historySize.width);
            if (isHistoryViewerForceAdditionalColumnsUsage()) {
                history.getSoftWrapModel().forceAdditionalColumnsUsage();
                input.getSettings().setAdditionalColumnsCount(
                        2 + (width - inputSize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, input));
                history.getSettings().setAdditionalColumnsCount(
                        2 + (width - historySize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, history));
            }

            // deal with height, WEB-11122 we cannot trust editor width  it could be 0 in case of soft wrap even if editor has text
            if (history.getDocument().getLineCount() == 0) {
                historySize.height = 0;
            }

            int minHistoryHeight = historySize.height > 0
                    ? (getMinHistoryLineCount() * history.getLineHeight()
                            + (myShowSeparatorLine ? SEPARATOR_THICKNESS : 0))
                    : 0;
            int minInputHeight = input.isViewer() ? 0 : input.getLineHeight();
            final int inputPreferredHeight = input.isViewer() ? 0 : Math.max(minInputHeight, inputSize.height);
            final int historyPreferredHeight = Math.max(minHistoryHeight, historySize.height);
            if (panelSize.height < minInputHeight) {
                newInputHeight = panelSize.height;
            } else if (panelSize.height < inputPreferredHeight) {
                newInputHeight = panelSize.height - minHistoryHeight;
            } else if (panelSize.height < (inputPreferredHeight + historyPreferredHeight)
                    || inputPreferredHeight == 0) {
                newInputHeight = inputPreferredHeight;
            } else {
                newInputHeight = panelSize.height - historyPreferredHeight;
            }

            int newHistoryHeight = panelSize.height - newInputHeight;
            // apply
            input.getComponent().setBounds(0, newHistoryHeight, panelSize.width, newInputHeight);
            myForceScrollToEnd.compareAndSet(false, shouldScrollHistoryToEnd());
            history.getComponent().setBounds(0, 0, panelSize.width, newHistoryHeight);
        }
    }
}