blob: 711184d9b70507742bcae1a7ec6620284570afa3 [file] [log] [blame]
/*
* 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.FileTypes;
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", FileTypes.PLAIN_TEXT, "");
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;
}
if (!isValid()) return;
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);
}
}
@NotNull
public Editor getCurrentEditor() {
return ObjectUtils.notNull(myCurrentEditor, myConsoleEditor);
}
@NotNull
public Language getLanguage() {
return ObjectUtils.assertNotNull(myVirtualFile.getLanguage());
}
public boolean isValid() {
return myVirtualFile.isValid() && myProject.isOpen();
}
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);
}
}
}