From 44a51c200d1b267153abba14980dcb89d8a13472 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Sun, 7 Nov 2010 12:17:00 +0300 Subject: [PATCH] line separators --- .../console/LanguageConsoleImpl.java | 1212 +++--- .../execution/impl/ConsoleViewImpl.java | 3520 ++++++++--------- .../execution/process/OSProcessHandler.java | 618 +-- .../src/messages/ExecutionBundle.properties | 606 +-- .../xdebugger/impl/ui/XDebugSessionTab.java | 510 +-- .../impl/ui/tree/SetValueInplaceEditor.java | 170 +- 6 files changed, 3318 insertions(+), 3318 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index 012eb2e143b8..524777f3eb24 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -1,606 +1,606 @@ -/* - * Copyright 2000-2010 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.execution.ui.ConsoleViewContentType; -import com.intellij.ide.DataManager; -import com.intellij.ide.impl.TypeSafeDataProviderAdapter; -import com.intellij.lang.Language; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.editor.*; -import com.intellij.openapi.editor.actions.EditorActionUtil; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.event.*; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.RangeHighlighterEx; -import com.intellij.openapi.editor.ex.util.EditorUtil; -import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; -import com.intellij.openapi.editor.impl.DocumentImpl; -import com.intellij.openapi.editor.impl.EditorFactoryImpl; -import com.intellij.openapi.editor.impl.EditorImpl; -import com.intellij.openapi.editor.markup.*; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.fileEditor.TextEditor; -import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; -import com.intellij.openapi.fileEditor.impl.EditorWindow; -import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; -import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileFactory; -import com.intellij.psi.impl.PsiDocumentManagerImpl; -import com.intellij.psi.impl.PsiFileFactoryImpl; -import com.intellij.psi.impl.PsiManagerEx; -import com.intellij.testFramework.LightVirtualFile; -import com.intellij.ui.SideBorder; -import com.intellij.util.FileContentUtil; -import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.update.MergingUpdateQueue; -import com.intellij.util.ui.update.Update; -import org.jetbrains.annotations.NonNls; - -import javax.swing.FocusManager; -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author Gregory.Shrago - */ -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; - protected PsiFile myFile; - - private final JPanel myPanel = new JPanel(new BorderLayout()); - - private String myTitle; - private String myPrompt = "> "; - private final LightVirtualFile myHistoryFile; - - private Editor myCurrentEditor; - - private final AtomicBoolean myForceScrollToEnd = new AtomicBoolean(false); - private final MergingUpdateQueue myUpdateQueue; - private Runnable myUiUpdateRunnable; - - private Editor myFullEditor; - private ActionGroup myFullEditorActions; - private final boolean myDoSaveErrorsToHistory; - - public LanguageConsoleImpl(final Project project, String title, final Language language, final boolean doSaveErrorsToHistory) { - myProject = project; - myTitle = title; - myDoSaveErrorsToHistory = doSaveErrorsToHistory; - installEditorFactoryListener(); - final EditorFactory editorFactory = EditorFactory.getInstance(); - myHistoryFile = new LightVirtualFile(getTitle() + ".history.txt", StdFileTypes.PLAIN_TEXT, ""); - myEditorDocument = editorFactory.createDocument(""); - setLanguage(language); - myConsoleEditor = (EditorEx)editorFactory.createEditor(myEditorDocument, myProject); - myConsoleEditor.setBackgroundColor(myConsoleEditor.getColorsScheme().getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); - myCurrentEditor = myConsoleEditor; - myHistoryViewer = (EditorEx)editorFactory.createViewer(((EditorFactoryImpl)editorFactory).createDocument(true), myProject); - myHistoryViewer.setBackgroundColor(myHistoryViewer.getColorsScheme().getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); - myPanel.add(myHistoryViewer.getComponent(), BorderLayout.NORTH); - myPanel.add(myConsoleEditor.getComponent(), BorderLayout.CENTER); - setupComponents(); - myPanel.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new TypeSafeDataProviderAdapter(this)); - myUpdateQueue = new MergingUpdateQueue("ConsoleUpdateQueue", 300, true, null); - Disposer.register(this, myUpdateQueue); - myPanel.addComponentListener(new ComponentAdapter() { - public void componentResized(ComponentEvent e) { - try { - myHistoryViewer.getScrollingModel().disableAnimation(); - updateSizes(true); - } - finally { - myHistoryViewer.getScrollingModel().enableAnimation(); - } - } - - public void componentShown(ComponentEvent e) { - componentResized(e); - } - }); - } - - public void setFullEditorMode(boolean fullEditorMode) { - if (myFullEditor != null == fullEditorMode) return; - final VirtualFile virtualFile = myFile.getVirtualFile(); - assert virtualFile != null; - final FileEditorManagerEx fileManager = FileEditorManagerEx.getInstanceEx(getProject()); - if (!fullEditorMode) { - fileManager.closeFile(virtualFile); - myFullEditor = null; - myPanel.removeAll(); - myPanel.add(myHistoryViewer.getComponent(), BorderLayout.NORTH); - myPanel.add(myConsoleEditor.getComponent(), BorderLayout.CENTER); - - myHistoryViewer.setHorizontalScrollbarVisible(false); - } - else { - myPanel.removeAll(); - myPanel.add(myHistoryViewer.getComponent(), BorderLayout.CENTER); - myFullEditor = fileManager.openTextEditor(new OpenFileDescriptor(getProject(), virtualFile, 0), true); - assert myFullEditor != null; - configureFullEditor(); - EditorWindow editorWindow = EditorWindow.DATA_KEY.getData(DataManager.getInstance().getDataContext(myFullEditor.getComponent())); - if (editorWindow == null) { - editorWindow = fileManager.getCurrentWindow(); - } - if (editorWindow != null) { - editorWindow.setFilePinned(virtualFile, true); - } - - myHistoryViewer.setHorizontalScrollbarVisible(true); - } - } - - public void setFullEditorActions(ActionGroup actionGroup) { - myFullEditorActions = actionGroup; - configureFullEditor(); - } - - private void setupComponents() { - setupEditorDefault(myConsoleEditor); - setupEditorDefault(myHistoryViewer); - setPrompt(myPrompt); - myConsoleEditor.addEditorMouseListener(EditorActionUtil.createEditorPopupHandler(IdeActions.GROUP_CUT_COPY_PASTE)); - if (SEPARATOR_THICKNESS > 0) { - myHistoryViewer.getComponent().setBorder(new SideBorder(Color.LIGHT_GRAY, SideBorder.BOTTOM)); - } - myHistoryViewer.getComponent().setMinimumSize(new Dimension(0, 0)); - myHistoryViewer.getComponent().setPreferredSize(new Dimension(0, 0)); - myConsoleEditor.getSettings().setAdditionalLinesCount(2); - myConsoleEditor.setHighlighter(EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, myFile.getVirtualFile())); - myHistoryViewer.setCaretEnabled(false); - myConsoleEditor.setHorizontalScrollbarVisible(true); - final VisibleAreaListener areaListener = new VisibleAreaListener() { - 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(); - } - } - } - }; - myConsoleEditor.getScrollingModel().addVisibleAreaListener(areaListener); - 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() { - public void keyTyped(KeyEvent event) { - if (myFullEditor == null && UIUtil.isReallyTypedEvent(event)) { - myConsoleEditor.getContentComponent().requestFocus(); - myConsoleEditor.processKeyTyped(event); - } - } - }); - for (AnAction action : createActions()) { - action.registerCustomShortcutSet(action.getShortcutSet(), myConsoleEditor.getComponent()); - } - registerActionShortcuts(myHistoryViewer.getComponent()); - } - - protected AnAction[] createActions() { - return AnAction.EMPTY_ARRAY; - } - - private static void setupEditorDefault(EditorEx editor) { - editor.getContentComponent().setFocusCycleRoot(false); - editor.setHorizontalScrollbarVisible(false); - editor.setVerticalScrollbarVisible(true); - editor.getColorsScheme().setColor(EditorColors.CARET_ROW_COLOR, null); - editor.setBorder(null); - editor.getContentComponent().setFocusCycleRoot(false); - - final EditorSettings editorSettings = editor.getSettings(); - editorSettings.setAdditionalLinesCount(0); - editorSettings.setAdditionalColumnsCount(1); - editorSettings.setRightMarginShown(false); - editorSettings.setFoldingOutlineShown(true); - editorSettings.setLineNumbersShown(false); - editorSettings.setLineMarkerAreaShown(false); - editorSettings.setIndentGuidesShown(false); - editorSettings.setVirtualSpace(false); - editorSettings.setLineCursorWidth(1); - } - - public void setUiUpdateRunnable(Runnable uiUpdateRunnable) { - assert myUiUpdateRunnable == null : "can be set only once"; - myUiUpdateRunnable = uiUpdateRunnable; - } - - public void flushAllUiUpdates() { - myUpdateQueue.flush(); - } - - public LightVirtualFile getHistoryFile() { - return myHistoryFile; - } - - public String getPrompt() { - return myPrompt; - } - - public void setPrompt(String prompt) { - myPrompt = prompt; - ((EditorImpl)myConsoleEditor).setPrefixTextAndAttributes(myPrompt, ConsoleViewContentType.USER_INPUT.getAttributes()); - } - - public PsiFile getFile() { - return myFile; - } - - public EditorEx getHistoryViewer() { - return myHistoryViewer; - } - - public Document getEditorDocument() { - return myEditorDocument; - } - - public EditorEx getConsoleEditor() { - return myConsoleEditor; - } - - public Project getProject() { - return myProject; - } - - public String getTitle() { - return myTitle; - } - - public void setTitle(String title) { - this.myTitle = title; - } - - public void addToHistory(final String text, final TextAttributes attributes) { - printToHistory(text, attributes); - } - - public Editor getFullEditor() { - return myFullEditor; - } - - public void printToHistory(String text, final TextAttributes attributes) { - text = StringUtil.convertLineSeparators(text); - final boolean scrollToEnd = shouldScrollHistoryToEnd(); - final Document history = myHistoryViewer.getDocument(); - final MarkupModel markupModel = history.getMarkupModel(myProject); - final int offset = history.getTextLength(); - history.insertString(offset, text); - markupModel.addRangeHighlighter(offset, - history.getTextLength(), - HighlighterLayer.SYNTAX, - attributes, - HighlighterTargetArea.EXACT_RANGE); - queueUiUpdate(scrollToEnd); - } - - public String addCurrentToHistory(final TextRange textRange, final boolean erase) { - final Ref ref = Ref.create(""); - final boolean scrollToEnd = shouldScrollHistoryToEnd(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - ref.set(addTextRangeToHistory(textRange, myConsoleEditor)); - if (erase) { - myConsoleEditor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset()); - } - } - }); - queueUiUpdate(scrollToEnd); - return ref.get(); - } - - public boolean shouldScrollHistoryToEnd() { - final Rectangle visibleArea = myHistoryViewer.getScrollingModel().getVisibleArea(); - final int lineNum = (visibleArea.y + visibleArea.height + myHistoryViewer.getLineHeight()) / myHistoryViewer.getLineHeight(); - final int lineCount = myHistoryViewer.getDocument().getLineCount(); - return lineNum == lineCount; - } - - private void scrollHistoryToEnd() { - final int lineCount = myHistoryViewer.getDocument().getLineCount(); - if (lineCount == 0) return; - myHistoryViewer.getCaretModel().moveToOffset(myHistoryViewer.getDocument().getLineStartOffset(lineCount - 1)); - myHistoryViewer.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); - } - - private String addTextRangeToHistory(TextRange textRange, final EditorEx consoleEditor) { - final DocumentImpl history = (DocumentImpl)myHistoryViewer.getDocument(); - final MarkupModel markupModel = history.getMarkupModel(myProject); - history.insertString(history.getTextLength(), myPrompt); - markupModel.addRangeHighlighter(history.getTextLength() - myPrompt.length(), history.getTextLength(), HighlighterLayer.SYNTAX, - ConsoleViewContentType.USER_INPUT.getAttributes(), - HighlighterTargetArea.EXACT_RANGE); - - int offset = history.getTextLength(); - final String text = consoleEditor.getDocument().getText(textRange); - history.insertString(offset, text); - offset = history.getTextLength() - text.length(); //offset can be changed after text trimming after insert due to buffer constraints - final HighlighterIterator iterator = consoleEditor.getHighlighter().createIterator(0); - while (!iterator.atEnd()) { - final int localOffset = textRange.getStartOffset(); - final int start = Math.max(iterator.getStart(), localOffset) - localOffset; - final int end = Math.min(iterator.getEnd(), textRange.getEndOffset()) - localOffset; - markupModel.addRangeHighlighter(start + offset, end + offset, HighlighterLayer.SYNTAX, iterator.getTextAttributes(), - HighlighterTargetArea.EXACT_RANGE); - - iterator.advance(); - } - if (myDoSaveErrorsToHistory) { - duplicateHighlighters(markupModel, consoleEditor.getDocument().getMarkupModel(myProject), offset, textRange); - duplicateHighlighters(markupModel, consoleEditor.getMarkupModel(), offset, textRange); - } - if (!text.endsWith("\n")) history.insertString(history.getTextLength(), "\n"); - return text; - } - - private static void duplicateHighlighters(MarkupModel to, MarkupModel from, int offset, TextRange textRange) { - for (RangeHighlighter rangeHighlighter : from.getAllHighlighters()) { - 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()); - } - } - - public JComponent getComponent() { - return myPanel; - } - - public void queueUiUpdate(final boolean forceScrollToEnd) { - myForceScrollToEnd.compareAndSet(false, forceScrollToEnd); - myUpdateQueue.queue(new Update("UpdateUi") { - public void run() { - if (Disposer.isDisposed(LanguageConsoleImpl.this)) return; - updateSizes(myForceScrollToEnd.getAndSet(false)); - if (myUiUpdateRunnable != null) { - ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); - } - } - }); - } - - private void updateSizes(boolean forceScrollToEnd) { - if (myFullEditor != null) return; - final Dimension panelSize = myPanel.getSize(); - final Dimension historyContentSize = myHistoryViewer.getContentSize(); - final Dimension contentSize = myConsoleEditor.getContentSize(); - final Dimension newEditorSize = new Dimension(); - final int minHistorySize = historyContentSize.height > 0 ? 2 * myHistoryViewer.getLineHeight() + SEPARATOR_THICKNESS : 0; - final int width = Math.max(contentSize.width, historyContentSize.width); - newEditorSize.height = Math.min(Math.max(panelSize.height - minHistorySize, 2 * myConsoleEditor.getLineHeight()), - contentSize.height + myConsoleEditor.getScrollPane().getHorizontalScrollBar().getHeight()); - newEditorSize.width = width + myConsoleEditor.getScrollPane().getHorizontalScrollBar().getHeight(); - myConsoleEditor.getSettings() - .setAdditionalColumnsCount(2 + (width - contentSize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, myConsoleEditor)); - myHistoryViewer.getSettings() - .setAdditionalColumnsCount(2 + (width - historyContentSize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, myHistoryViewer)); - - final Dimension editorSize = myConsoleEditor.getComponent().getSize(); - if (!editorSize.equals(newEditorSize)) { - myConsoleEditor.getComponent().setPreferredSize(newEditorSize); - } - final boolean scrollToEnd = forceScrollToEnd || shouldScrollHistoryToEnd(); - final Dimension newHistorySize = new Dimension( - width, Math.max(0, Math.min(minHistorySize == 0 ? 0 : historyContentSize.height + SEPARATOR_THICKNESS, - panelSize.height - newEditorSize.height))); - final Dimension historySize = myHistoryViewer.getComponent().getSize(); - if (!historySize.equals(newHistorySize)) { - myHistoryViewer.getComponent().setPreferredSize(newHistorySize); - } - myPanel.validate(); - if (scrollToEnd) scrollHistoryToEnd(); - } - - public void dispose() { - final EditorFactory editorFactory = EditorFactory.getInstance(); - editorFactory.releaseEditor(myConsoleEditor); - editorFactory.releaseEditor(myHistoryViewer); - - final VirtualFile virtualFile = myFile.getVirtualFile(); - assert virtualFile != null; - final FileEditorManager editorManager = FileEditorManager.getInstance(getProject()); - final boolean isOpen = editorManager.isFileOpen(virtualFile); - if (isOpen) { - editorManager.closeFile(virtualFile); - } - } - - public void calcData(DataKey key, DataSink sink) { - if (OpenFileDescriptor.NAVIGATE_IN_EDITOR == key) { - sink.put(OpenFileDescriptor.NAVIGATE_IN_EDITOR, myConsoleEditor); - return; - } - final Object o = - ((FileEditorManagerImpl)FileEditorManager.getInstance(getProject())).getData(key.getName(), myConsoleEditor, myFile.getVirtualFile()); - sink.put(key, o); - } - - private void installEditorFactoryListener() { - final EditorFactoryListener factoryListener = new EditorFactoryListener() { - public void editorCreated(final EditorFactoryEvent event) { - final Editor editor = event.getEditor(); - if (editor.getDocument() == myEditorDocument) { - if (myConsoleEditor != null) { - // i.e. if console is initialized - queueUiUpdate(false); - registerActionShortcuts(editor.getComponent()); - } - editor.getCaretModel().addCaretListener(new CaretListener() { - public void caretPositionChanged(CaretEvent e) { - queueUiUpdate(false); - } - }); - editor.getContentComponent().addFocusListener(new FocusListener() { - public void focusGained(final FocusEvent e) { - myCurrentEditor = editor; - } - - public void focusLost(final FocusEvent e) { - } - }); - } - } - - public void editorReleased(final EditorFactoryEvent event) { - if (event.getEditor().getDocument() == myEditorDocument) { - if (myUiUpdateRunnable != null) { - ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); - } - } - } - }; - EditorFactory.getInstance().addEditorFactoryListener(factoryListener, this); - } - - protected void registerActionShortcuts(JComponent component) { - final ArrayList actionList = - (ArrayList)myConsoleEditor.getComponent().getClientProperty(AnAction.ourClientProperty); - if (actionList != null) { - for (AnAction anAction : actionList) { - anAction.registerCustomShortcutSet(anAction.getShortcutSet(), component); - } - } - } - - public Editor getCurrentEditor() { - return myCurrentEditor; - } - - public void setLanguage(Language language) { - final PsiFile prevFile = myFile; - if (prevFile != null) { - final VirtualFile file = prevFile.getVirtualFile(); - assert file instanceof LightVirtualFile; - ((LightVirtualFile)file).setValid(false); - ((PsiManagerEx)prevFile.getManager()).getFileManager().setViewProvider(file, null); - } - - final FileType type = language.getAssociatedFileType(); - @NonNls final String name = getTitle(); - final LightVirtualFile newVFile = new LightVirtualFile(name, language, myEditorDocument.getText()); - FileDocumentManagerImpl.registerDocument(myEditorDocument, newVFile); - myFile = ((PsiFileFactoryImpl)PsiFileFactory.getInstance(myProject)).trySetupPsiForFile(newVFile, language, true, false); - if (myFile == null) { - throw new AssertionError("file=null, name=" + name + ", language=" + language.getDisplayName()); - } - PsiDocumentManagerImpl.cachePsi(myEditorDocument, myFile); - FileContentUtil.reparseFiles(myProject, Collections.singletonList(newVFile), false); - - if (prevFile != null) { - final FileEditorManager editorManager = FileEditorManager.getInstance(getProject()); - final VirtualFile file = prevFile.getVirtualFile(); - if (file != null && myFullEditor != null) { - myFullEditor = null; - final FileEditor prevEditor = editorManager.getSelectedEditor(file); - final boolean focusEditor; - final int offset; - if (prevEditor != null) { - offset = prevEditor instanceof TextEditor ? ((TextEditor)prevEditor).getEditor().getCaretModel().getOffset() : 0; - final Component owner = FocusManager.getCurrentManager().getFocusOwner(); - focusEditor = owner != null && SwingUtilities.isDescendingFrom(owner, prevEditor.getComponent()); - } - else { - focusEditor = false; - offset = 0; - } - editorManager.closeFile(file); - myFullEditor = editorManager.openTextEditor(new OpenFileDescriptor(getProject(), newVFile, offset), focusEditor); - configureFullEditor(); - ((FileEditorManagerEx)editorManager).getCurrentWindow().setFilePinned(newVFile, true); - } - } - } - - private void configureFullEditor() { - if (myFullEditor == null || myFullEditorActions == null) return; - final JPanel header = new JPanel(new BorderLayout()); - header.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, myFullEditorActions, true).getComponent(), - BorderLayout.EAST); - myFullEditor.setHeaderComponent(header); - myFullEditor.getSettings().setLineMarkerAreaShown(false); - } - - public void setInputText(final String query) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - myConsoleEditor.getDocument().setText(query); - } - }); - } - - public static void printToConsole(final LanguageConsoleImpl console, - final String string, - final ConsoleViewContentType mainType, - ConsoleViewContentType additionalType) { - final TextAttributes mainAttributes = mainType.getAttributes(); - final TextAttributes attributes; - if (additionalType == null) { - attributes = mainAttributes; - } - else { - attributes = additionalType.getAttributes().clone(); - attributes.setBackgroundColor(mainAttributes.getBackgroundColor()); - } - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - console.printToHistory(string, attributes); - } - }, ModalityState.stateForComponent(console.getComponent())); - } -} +/* + * Copyright 2000-2010 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.execution.ui.ConsoleViewContentType; +import com.intellij.ide.DataManager; +import com.intellij.ide.impl.TypeSafeDataProviderAdapter; +import com.intellij.lang.Language; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.actions.EditorActionUtil; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.event.*; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.RangeHighlighterEx; +import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.editor.impl.DocumentImpl; +import com.intellij.openapi.editor.impl.EditorFactoryImpl; +import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.editor.markup.*; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; +import com.intellij.openapi.fileEditor.impl.EditorWindow; +import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.impl.PsiDocumentManagerImpl; +import com.intellij.psi.impl.PsiFileFactoryImpl; +import com.intellij.psi.impl.PsiManagerEx; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.ui.SideBorder; +import com.intellij.util.FileContentUtil; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.NonNls; + +import javax.swing.FocusManager; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author Gregory.Shrago + */ +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; + protected PsiFile myFile; + + private final JPanel myPanel = new JPanel(new BorderLayout()); + + private String myTitle; + private String myPrompt = "> "; + private final LightVirtualFile myHistoryFile; + + private Editor myCurrentEditor; + + private final AtomicBoolean myForceScrollToEnd = new AtomicBoolean(false); + private final MergingUpdateQueue myUpdateQueue; + private Runnable myUiUpdateRunnable; + + private Editor myFullEditor; + private ActionGroup myFullEditorActions; + private final boolean myDoSaveErrorsToHistory; + + public LanguageConsoleImpl(final Project project, String title, final Language language, final boolean doSaveErrorsToHistory) { + myProject = project; + myTitle = title; + myDoSaveErrorsToHistory = doSaveErrorsToHistory; + installEditorFactoryListener(); + final EditorFactory editorFactory = EditorFactory.getInstance(); + myHistoryFile = new LightVirtualFile(getTitle() + ".history.txt", StdFileTypes.PLAIN_TEXT, ""); + myEditorDocument = editorFactory.createDocument(""); + setLanguage(language); + myConsoleEditor = (EditorEx)editorFactory.createEditor(myEditorDocument, myProject); + myConsoleEditor.setBackgroundColor(myConsoleEditor.getColorsScheme().getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); + myCurrentEditor = myConsoleEditor; + myHistoryViewer = (EditorEx)editorFactory.createViewer(((EditorFactoryImpl)editorFactory).createDocument(true), myProject); + myHistoryViewer.setBackgroundColor(myHistoryViewer.getColorsScheme().getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); + myPanel.add(myHistoryViewer.getComponent(), BorderLayout.NORTH); + myPanel.add(myConsoleEditor.getComponent(), BorderLayout.CENTER); + setupComponents(); + myPanel.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new TypeSafeDataProviderAdapter(this)); + myUpdateQueue = new MergingUpdateQueue("ConsoleUpdateQueue", 300, true, null); + Disposer.register(this, myUpdateQueue); + myPanel.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + try { + myHistoryViewer.getScrollingModel().disableAnimation(); + updateSizes(true); + } + finally { + myHistoryViewer.getScrollingModel().enableAnimation(); + } + } + + public void componentShown(ComponentEvent e) { + componentResized(e); + } + }); + } + + public void setFullEditorMode(boolean fullEditorMode) { + if (myFullEditor != null == fullEditorMode) return; + final VirtualFile virtualFile = myFile.getVirtualFile(); + assert virtualFile != null; + final FileEditorManagerEx fileManager = FileEditorManagerEx.getInstanceEx(getProject()); + if (!fullEditorMode) { + fileManager.closeFile(virtualFile); + myFullEditor = null; + myPanel.removeAll(); + myPanel.add(myHistoryViewer.getComponent(), BorderLayout.NORTH); + myPanel.add(myConsoleEditor.getComponent(), BorderLayout.CENTER); + + myHistoryViewer.setHorizontalScrollbarVisible(false); + } + else { + myPanel.removeAll(); + myPanel.add(myHistoryViewer.getComponent(), BorderLayout.CENTER); + myFullEditor = fileManager.openTextEditor(new OpenFileDescriptor(getProject(), virtualFile, 0), true); + assert myFullEditor != null; + configureFullEditor(); + EditorWindow editorWindow = EditorWindow.DATA_KEY.getData(DataManager.getInstance().getDataContext(myFullEditor.getComponent())); + if (editorWindow == null) { + editorWindow = fileManager.getCurrentWindow(); + } + if (editorWindow != null) { + editorWindow.setFilePinned(virtualFile, true); + } + + myHistoryViewer.setHorizontalScrollbarVisible(true); + } + } + + public void setFullEditorActions(ActionGroup actionGroup) { + myFullEditorActions = actionGroup; + configureFullEditor(); + } + + private void setupComponents() { + setupEditorDefault(myConsoleEditor); + setupEditorDefault(myHistoryViewer); + setPrompt(myPrompt); + myConsoleEditor.addEditorMouseListener(EditorActionUtil.createEditorPopupHandler(IdeActions.GROUP_CUT_COPY_PASTE)); + if (SEPARATOR_THICKNESS > 0) { + myHistoryViewer.getComponent().setBorder(new SideBorder(Color.LIGHT_GRAY, SideBorder.BOTTOM)); + } + myHistoryViewer.getComponent().setMinimumSize(new Dimension(0, 0)); + myHistoryViewer.getComponent().setPreferredSize(new Dimension(0, 0)); + myConsoleEditor.getSettings().setAdditionalLinesCount(2); + myConsoleEditor.setHighlighter(EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, myFile.getVirtualFile())); + myHistoryViewer.setCaretEnabled(false); + myConsoleEditor.setHorizontalScrollbarVisible(true); + final VisibleAreaListener areaListener = new VisibleAreaListener() { + 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(); + } + } + } + }; + myConsoleEditor.getScrollingModel().addVisibleAreaListener(areaListener); + 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() { + public void keyTyped(KeyEvent event) { + if (myFullEditor == null && UIUtil.isReallyTypedEvent(event)) { + myConsoleEditor.getContentComponent().requestFocus(); + myConsoleEditor.processKeyTyped(event); + } + } + }); + for (AnAction action : createActions()) { + action.registerCustomShortcutSet(action.getShortcutSet(), myConsoleEditor.getComponent()); + } + registerActionShortcuts(myHistoryViewer.getComponent()); + } + + protected AnAction[] createActions() { + return AnAction.EMPTY_ARRAY; + } + + private static void setupEditorDefault(EditorEx editor) { + editor.getContentComponent().setFocusCycleRoot(false); + editor.setHorizontalScrollbarVisible(false); + editor.setVerticalScrollbarVisible(true); + editor.getColorsScheme().setColor(EditorColors.CARET_ROW_COLOR, null); + editor.setBorder(null); + editor.getContentComponent().setFocusCycleRoot(false); + + final EditorSettings editorSettings = editor.getSettings(); + editorSettings.setAdditionalLinesCount(0); + editorSettings.setAdditionalColumnsCount(1); + editorSettings.setRightMarginShown(false); + editorSettings.setFoldingOutlineShown(true); + editorSettings.setLineNumbersShown(false); + editorSettings.setLineMarkerAreaShown(false); + editorSettings.setIndentGuidesShown(false); + editorSettings.setVirtualSpace(false); + editorSettings.setLineCursorWidth(1); + } + + public void setUiUpdateRunnable(Runnable uiUpdateRunnable) { + assert myUiUpdateRunnable == null : "can be set only once"; + myUiUpdateRunnable = uiUpdateRunnable; + } + + public void flushAllUiUpdates() { + myUpdateQueue.flush(); + } + + public LightVirtualFile getHistoryFile() { + return myHistoryFile; + } + + public String getPrompt() { + return myPrompt; + } + + public void setPrompt(String prompt) { + myPrompt = prompt; + ((EditorImpl)myConsoleEditor).setPrefixTextAndAttributes(myPrompt, ConsoleViewContentType.USER_INPUT.getAttributes()); + } + + public PsiFile getFile() { + return myFile; + } + + public EditorEx getHistoryViewer() { + return myHistoryViewer; + } + + public Document getEditorDocument() { + return myEditorDocument; + } + + public EditorEx getConsoleEditor() { + return myConsoleEditor; + } + + public Project getProject() { + return myProject; + } + + public String getTitle() { + return myTitle; + } + + public void setTitle(String title) { + this.myTitle = title; + } + + public void addToHistory(final String text, final TextAttributes attributes) { + printToHistory(text, attributes); + } + + public Editor getFullEditor() { + return myFullEditor; + } + + public void printToHistory(String text, final TextAttributes attributes) { + text = StringUtil.convertLineSeparators(text); + final boolean scrollToEnd = shouldScrollHistoryToEnd(); + final Document history = myHistoryViewer.getDocument(); + final MarkupModel markupModel = history.getMarkupModel(myProject); + final int offset = history.getTextLength(); + history.insertString(offset, text); + markupModel.addRangeHighlighter(offset, + history.getTextLength(), + HighlighterLayer.SYNTAX, + attributes, + HighlighterTargetArea.EXACT_RANGE); + queueUiUpdate(scrollToEnd); + } + + public String addCurrentToHistory(final TextRange textRange, final boolean erase) { + final Ref ref = Ref.create(""); + final boolean scrollToEnd = shouldScrollHistoryToEnd(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + ref.set(addTextRangeToHistory(textRange, myConsoleEditor)); + if (erase) { + myConsoleEditor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset()); + } + } + }); + queueUiUpdate(scrollToEnd); + return ref.get(); + } + + public boolean shouldScrollHistoryToEnd() { + final Rectangle visibleArea = myHistoryViewer.getScrollingModel().getVisibleArea(); + final int lineNum = (visibleArea.y + visibleArea.height + myHistoryViewer.getLineHeight()) / myHistoryViewer.getLineHeight(); + final int lineCount = myHistoryViewer.getDocument().getLineCount(); + return lineNum == lineCount; + } + + private void scrollHistoryToEnd() { + final int lineCount = myHistoryViewer.getDocument().getLineCount(); + if (lineCount == 0) return; + myHistoryViewer.getCaretModel().moveToOffset(myHistoryViewer.getDocument().getLineStartOffset(lineCount - 1)); + myHistoryViewer.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); + } + + private String addTextRangeToHistory(TextRange textRange, final EditorEx consoleEditor) { + final DocumentImpl history = (DocumentImpl)myHistoryViewer.getDocument(); + final MarkupModel markupModel = history.getMarkupModel(myProject); + history.insertString(history.getTextLength(), myPrompt); + markupModel.addRangeHighlighter(history.getTextLength() - myPrompt.length(), history.getTextLength(), HighlighterLayer.SYNTAX, + ConsoleViewContentType.USER_INPUT.getAttributes(), + HighlighterTargetArea.EXACT_RANGE); + + int offset = history.getTextLength(); + final String text = consoleEditor.getDocument().getText(textRange); + history.insertString(offset, text); + offset = history.getTextLength() - text.length(); //offset can be changed after text trimming after insert due to buffer constraints + final HighlighterIterator iterator = consoleEditor.getHighlighter().createIterator(0); + while (!iterator.atEnd()) { + final int localOffset = textRange.getStartOffset(); + final int start = Math.max(iterator.getStart(), localOffset) - localOffset; + final int end = Math.min(iterator.getEnd(), textRange.getEndOffset()) - localOffset; + markupModel.addRangeHighlighter(start + offset, end + offset, HighlighterLayer.SYNTAX, iterator.getTextAttributes(), + HighlighterTargetArea.EXACT_RANGE); + + iterator.advance(); + } + if (myDoSaveErrorsToHistory) { + duplicateHighlighters(markupModel, consoleEditor.getDocument().getMarkupModel(myProject), offset, textRange); + duplicateHighlighters(markupModel, consoleEditor.getMarkupModel(), offset, textRange); + } + if (!text.endsWith("\n")) history.insertString(history.getTextLength(), "\n"); + return text; + } + + private static void duplicateHighlighters(MarkupModel to, MarkupModel from, int offset, TextRange textRange) { + for (RangeHighlighter rangeHighlighter : from.getAllHighlighters()) { + 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()); + } + } + + public JComponent getComponent() { + return myPanel; + } + + public void queueUiUpdate(final boolean forceScrollToEnd) { + myForceScrollToEnd.compareAndSet(false, forceScrollToEnd); + myUpdateQueue.queue(new Update("UpdateUi") { + public void run() { + if (Disposer.isDisposed(LanguageConsoleImpl.this)) return; + updateSizes(myForceScrollToEnd.getAndSet(false)); + if (myUiUpdateRunnable != null) { + ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); + } + } + }); + } + + private void updateSizes(boolean forceScrollToEnd) { + if (myFullEditor != null) return; + final Dimension panelSize = myPanel.getSize(); + final Dimension historyContentSize = myHistoryViewer.getContentSize(); + final Dimension contentSize = myConsoleEditor.getContentSize(); + final Dimension newEditorSize = new Dimension(); + final int minHistorySize = historyContentSize.height > 0 ? 2 * myHistoryViewer.getLineHeight() + SEPARATOR_THICKNESS : 0; + final int width = Math.max(contentSize.width, historyContentSize.width); + newEditorSize.height = Math.min(Math.max(panelSize.height - minHistorySize, 2 * myConsoleEditor.getLineHeight()), + contentSize.height + myConsoleEditor.getScrollPane().getHorizontalScrollBar().getHeight()); + newEditorSize.width = width + myConsoleEditor.getScrollPane().getHorizontalScrollBar().getHeight(); + myConsoleEditor.getSettings() + .setAdditionalColumnsCount(2 + (width - contentSize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, myConsoleEditor)); + myHistoryViewer.getSettings() + .setAdditionalColumnsCount(2 + (width - historyContentSize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, myHistoryViewer)); + + final Dimension editorSize = myConsoleEditor.getComponent().getSize(); + if (!editorSize.equals(newEditorSize)) { + myConsoleEditor.getComponent().setPreferredSize(newEditorSize); + } + final boolean scrollToEnd = forceScrollToEnd || shouldScrollHistoryToEnd(); + final Dimension newHistorySize = new Dimension( + width, Math.max(0, Math.min(minHistorySize == 0 ? 0 : historyContentSize.height + SEPARATOR_THICKNESS, + panelSize.height - newEditorSize.height))); + final Dimension historySize = myHistoryViewer.getComponent().getSize(); + if (!historySize.equals(newHistorySize)) { + myHistoryViewer.getComponent().setPreferredSize(newHistorySize); + } + myPanel.validate(); + if (scrollToEnd) scrollHistoryToEnd(); + } + + public void dispose() { + final EditorFactory editorFactory = EditorFactory.getInstance(); + editorFactory.releaseEditor(myConsoleEditor); + editorFactory.releaseEditor(myHistoryViewer); + + final VirtualFile virtualFile = myFile.getVirtualFile(); + assert virtualFile != null; + final FileEditorManager editorManager = FileEditorManager.getInstance(getProject()); + final boolean isOpen = editorManager.isFileOpen(virtualFile); + if (isOpen) { + editorManager.closeFile(virtualFile); + } + } + + public void calcData(DataKey key, DataSink sink) { + if (OpenFileDescriptor.NAVIGATE_IN_EDITOR == key) { + sink.put(OpenFileDescriptor.NAVIGATE_IN_EDITOR, myConsoleEditor); + return; + } + final Object o = + ((FileEditorManagerImpl)FileEditorManager.getInstance(getProject())).getData(key.getName(), myConsoleEditor, myFile.getVirtualFile()); + sink.put(key, o); + } + + private void installEditorFactoryListener() { + final EditorFactoryListener factoryListener = new EditorFactoryListener() { + public void editorCreated(final EditorFactoryEvent event) { + final Editor editor = event.getEditor(); + if (editor.getDocument() == myEditorDocument) { + if (myConsoleEditor != null) { + // i.e. if console is initialized + queueUiUpdate(false); + registerActionShortcuts(editor.getComponent()); + } + editor.getCaretModel().addCaretListener(new CaretListener() { + public void caretPositionChanged(CaretEvent e) { + queueUiUpdate(false); + } + }); + editor.getContentComponent().addFocusListener(new FocusListener() { + public void focusGained(final FocusEvent e) { + myCurrentEditor = editor; + } + + public void focusLost(final FocusEvent e) { + } + }); + } + } + + public void editorReleased(final EditorFactoryEvent event) { + if (event.getEditor().getDocument() == myEditorDocument) { + if (myUiUpdateRunnable != null) { + ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); + } + } + } + }; + EditorFactory.getInstance().addEditorFactoryListener(factoryListener, this); + } + + protected void registerActionShortcuts(JComponent component) { + final ArrayList actionList = + (ArrayList)myConsoleEditor.getComponent().getClientProperty(AnAction.ourClientProperty); + if (actionList != null) { + for (AnAction anAction : actionList) { + anAction.registerCustomShortcutSet(anAction.getShortcutSet(), component); + } + } + } + + public Editor getCurrentEditor() { + return myCurrentEditor; + } + + public void setLanguage(Language language) { + final PsiFile prevFile = myFile; + if (prevFile != null) { + final VirtualFile file = prevFile.getVirtualFile(); + assert file instanceof LightVirtualFile; + ((LightVirtualFile)file).setValid(false); + ((PsiManagerEx)prevFile.getManager()).getFileManager().setViewProvider(file, null); + } + + final FileType type = language.getAssociatedFileType(); + @NonNls final String name = getTitle(); + final LightVirtualFile newVFile = new LightVirtualFile(name, language, myEditorDocument.getText()); + FileDocumentManagerImpl.registerDocument(myEditorDocument, newVFile); + myFile = ((PsiFileFactoryImpl)PsiFileFactory.getInstance(myProject)).trySetupPsiForFile(newVFile, language, true, false); + if (myFile == null) { + throw new AssertionError("file=null, name=" + name + ", language=" + language.getDisplayName()); + } + PsiDocumentManagerImpl.cachePsi(myEditorDocument, myFile); + FileContentUtil.reparseFiles(myProject, Collections.singletonList(newVFile), false); + + if (prevFile != null) { + final FileEditorManager editorManager = FileEditorManager.getInstance(getProject()); + final VirtualFile file = prevFile.getVirtualFile(); + if (file != null && myFullEditor != null) { + myFullEditor = null; + final FileEditor prevEditor = editorManager.getSelectedEditor(file); + final boolean focusEditor; + final int offset; + if (prevEditor != null) { + offset = prevEditor instanceof TextEditor ? ((TextEditor)prevEditor).getEditor().getCaretModel().getOffset() : 0; + final Component owner = FocusManager.getCurrentManager().getFocusOwner(); + focusEditor = owner != null && SwingUtilities.isDescendingFrom(owner, prevEditor.getComponent()); + } + else { + focusEditor = false; + offset = 0; + } + editorManager.closeFile(file); + myFullEditor = editorManager.openTextEditor(new OpenFileDescriptor(getProject(), newVFile, offset), focusEditor); + configureFullEditor(); + ((FileEditorManagerEx)editorManager).getCurrentWindow().setFilePinned(newVFile, true); + } + } + } + + private void configureFullEditor() { + if (myFullEditor == null || myFullEditorActions == null) return; + final JPanel header = new JPanel(new BorderLayout()); + header.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, myFullEditorActions, true).getComponent(), + BorderLayout.EAST); + myFullEditor.setHeaderComponent(header); + myFullEditor.getSettings().setLineMarkerAreaShown(false); + } + + public void setInputText(final String query) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + myConsoleEditor.getDocument().setText(query); + } + }); + } + + public static void printToConsole(final LanguageConsoleImpl console, + final String string, + final ConsoleViewContentType mainType, + ConsoleViewContentType additionalType) { + final TextAttributes mainAttributes = mainType.getAttributes(); + final TextAttributes attributes; + if (additionalType == null) { + attributes = mainAttributes; + } + else { + attributes = additionalType.getAttributes().clone(); + attributes.setBackgroundColor(mainAttributes.getBackgroundColor()); + } + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + console.printToHistory(string, attributes); + } + }, ModalityState.stateForComponent(console.getComponent())); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 7e67b9432520..61ba08d2a6fd 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -1,1760 +1,1760 @@ -/* - * Copyright 2000-2009 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.impl; - -import com.intellij.codeInsight.navigation.IncrementalSearchHandler; -import com.intellij.execution.ConsoleFolding; -import com.intellij.execution.ExecutionBundle; -import com.intellij.execution.filters.*; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.ui.ConsoleView; -import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.execution.ui.ObservableConsoleView; -import com.intellij.ide.CommonActionsManager; -import com.intellij.ide.OccurenceNavigator; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.*; -import com.intellij.openapi.editor.actionSystem.*; -import com.intellij.openapi.editor.actions.ScrollToTheEndToolbarAction; -import com.intellij.openapi.editor.actions.ToggleUseSoftWrapsToolbarAction; -import com.intellij.openapi.editor.colors.CodeInsightColors; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.event.*; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; -import com.intellij.openapi.editor.ex.FoldingModelEx; -import com.intellij.openapi.editor.ex.MarkupModelEx; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.HighlighterClient; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; -import com.intellij.openapi.editor.impl.EditorFactoryImpl; -import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; -import com.intellij.openapi.editor.markup.HighlighterLayer; -import com.intellij.openapi.editor.markup.HighlighterTargetArea; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.ide.CopyPasteManager; -import com.intellij.openapi.keymap.Keymap; -import com.intellij.openapi.keymap.KeymapManager; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.text.LineTokenizer; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.pom.Navigatable; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileFactory; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.tree.IElementType; -import com.intellij.util.Alarm; -import com.intellij.util.EditorPopupHandler; -import com.intellij.util.LocalTimeCounter; -import com.intellij.util.containers.HashMap; -import com.intellij.util.text.CharArrayUtil; -import gnu.trove.TIntObjectHashMap; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; - -import javax.swing.*; -import java.awt.*; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import java.io.IOException; -import java.util.*; -import java.util.List; -import java.util.concurrent.CopyOnWriteArraySet; - -public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { - private @NonNls String CONSOLE_VIEW_POPUP_MENU = "ConsoleView.PopupMenu"; - private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.ConsoleViewImpl"); - - private static final int FLUSH_DELAY = 200; //TODO : make it an option - - private static final Key CONSOLE_VIEW_IN_EDITOR_VIEW = Key.create("CONSOLE_VIEW_IN_EDITOR_VIEW"); - - static { - final EditorActionManager actionManager = EditorActionManager.getInstance(); - final TypedAction typedAction = actionManager.getTypedAction(); - typedAction.setupHandler(new MyTypedHandler(typedAction.getHandler())); - } - - private final int CYCLIC_BUFFER_SIZE = getCycleBufferSize(); - private final CommandLineFolding myCommandLineFolding = new CommandLineFolding(); - - private final DisposedPsiManagerCheck myPsiDisposedCheck; - private final boolean isViewer; - - private ConsoleState myState = ConsoleState.NOT_STARTED; - private Computable myStateForUpdate; - - private static int getCycleBufferSize() { - final String cycleBufferSizeProperty = System.getProperty("idea.cycle.buffer.size"); - if (cycleBufferSizeProperty == null) return 1024 * 1024; - try { - return Integer.parseInt(cycleBufferSizeProperty) * 1024; - } - catch (NumberFormatException e) { - return 1024 * 1024; - } - } - - private final boolean USE_CYCLIC_BUFFER = useCycleBuffer(); - - private static boolean useCycleBuffer() { - final String useCycleBufferProperty = System.getProperty("idea.cycle.buffer.size"); - return useCycleBufferProperty == null || !"disabled".equalsIgnoreCase(useCycleBufferProperty); - } - - private static final int HYPERLINK_LAYER = HighlighterLayer.SELECTION - 123; - private final Alarm mySpareTimeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); - - private final CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); - private final Set myDeferredTypes = new HashSet(); - private final ArrayList customActions = new ArrayList(); - - @TestOnly - public Editor getEditor() { - return myEditor; - } - - public void scrollToEnd() { - if (myEditor == null) return; - myEditor.getCaretModel().moveToOffset(myEditor.getDocument().getTextLength()); - } - - public void foldImmediately() { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (myFlushAlarm.getActiveRequestCount() > 0) { - myFlushAlarm.cancelAllRequests(); - myFlushDeferredRunnable.run(); - } - - myFoldingAlarm.cancelAllRequests(); - - myPendingFoldRegions.clear(); - final FoldingModel model = myEditor.getFoldingModel(); - model.runBatchFoldingOperation(new Runnable() { - public void run() { - for (FoldRegion region : model.getAllFoldRegions()) { - model.removeFoldRegion(region); - } - } - }); - myFolding.clear(); - - updateFoldings(0, myEditor.getDocument().getLineCount() - 1, true); - } - - private static class TokenInfo { - private final ConsoleViewContentType contentType; - private int startOffset; - private int endOffset; - private final TextAttributes attributes; - - private TokenInfo(final ConsoleViewContentType contentType, final int startOffset, final int endOffset) { - this.contentType = contentType; - this.startOffset = startOffset; - this.endOffset = endOffset; - attributes = contentType.getAttributes(); - } - } - - private final Project myProject; - - private boolean myOutputPaused; - - private Editor myEditor; - - private final Object LOCK = new Object(); - - private int myContentSize; - private StringBuffer myDeferredOutput = new StringBuffer(); - private StringBuffer myDeferredUserInput = new StringBuffer(); - - private ArrayList myTokens = new ArrayList(); - private final Hyperlinks myHyperlinks = new Hyperlinks(); - private final TIntObjectHashMap myFolding = new TIntObjectHashMap(); - - private String myHelpId; - - private final Alarm myFlushUserInputAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, this); - private final Alarm myFlushAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); - private final Runnable myFlushDeferredRunnable = new Runnable() { - public void run() { - flushDeferredText(); - } - }; - - protected final CompositeFilter myPredefinedMessageFilter; - protected final CompositeFilter myCustomFilter; - - private final ArrayList myHistory = new ArrayList(); - private int myHistorySize = 20; - - private final ArrayList myConsoleInputListeners = new ArrayList(); - - private final Alarm myFoldingAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); - private final List myPendingFoldRegions = new ArrayList(); - - public void addConsoleUserInputListener(ConsoleInputListener consoleInputListener) { - myConsoleInputListeners.add(consoleInputListener); - } - - /** - * By default history works for one session. If - * you want to import previous session, set it up here. - * - * @param history where you can save history - */ - public void importHistory(Collection history) { - myHistory.clear(); - myHistory.addAll(history); - while (myHistory.size() > myHistorySize) { - myHistory.remove(0); - } - } - - public List getHistory() { - return Collections.unmodifiableList(myHistory); - } - - public void setHistorySize(int historySize) { - myHistorySize = historySize; - } - - public int getHistorySize() { - return myHistorySize; - } - - private FileType myFileType; - - /** - * Use it for custom highlighting for user text. - * This will be highlighted as appropriate file to this file type. - * - * @param fileType according to which use highlighting - */ - public void setFileType(FileType fileType) { - myFileType = fileType; - } - - public ConsoleViewImpl(final Project project, boolean viewer) { - this(project, viewer, null); - } - - public ConsoleViewImpl(final Project project, boolean viewer, FileType fileType) { - this(project, GlobalSearchScope.allScope(project), viewer, fileType); - } - - - public ConsoleViewImpl(final Project project, GlobalSearchScope searchScope, boolean viewer, FileType fileType) { - super(new BorderLayout()); - isViewer = viewer; - myPsiDisposedCheck = new DisposedPsiManagerCheck(project); - myProject = project; - myFileType = fileType; - - myCustomFilter = new CompositeFilter(project); - myPredefinedMessageFilter = new CompositeFilter(project); - for (ConsoleFilterProvider eachProvider : Extensions.getExtensions(ConsoleFilterProvider.FILTER_PROVIDERS)) { - Filter[] filters = eachProvider instanceof ConsoleFilterProviderEx - ? ((ConsoleFilterProviderEx)eachProvider).getDefaultFilters(project, searchScope) - : eachProvider.getDefaultFilters(project); - for (Filter filter : filters) { - myPredefinedMessageFilter.addFilter(filter); - } - } - - Disposer.register(project, this); - } - - public void attachToProcess(final ProcessHandler processHandler) { - myState = myState.attachTo(this, processHandler); - } - - public void clear() { - assertIsDispatchThread(); - - final Document document; - synchronized (LOCK) { - myContentSize = 0; - if (USE_CYCLIC_BUFFER) { - myDeferredOutput = new StringBuffer(Math.min(myDeferredOutput.length(), CYCLIC_BUFFER_SIZE)); - } - else { - myDeferredOutput = new StringBuffer(); - } - myDeferredTypes.clear(); - myDeferredUserInput = new StringBuffer(); - myHyperlinks.clear(); - myTokens.clear(); - if (myEditor == null) return; - myEditor.getMarkupModel().removeAllHighlighters(); - document = myEditor.getDocument(); - myFoldingAlarm.cancelAllRequests(); - } - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - document.deleteString(0, document.getTextLength()); - } - }, null, DocCommandGroupId.noneGroupId(document)); - } - - public void scrollTo(final int offset) { - assertIsDispatchThread(); - flushDeferredText(); - if (myEditor == null) return; - int moveOffset = offset; - if (USE_CYCLIC_BUFFER && moveOffset >= myEditor.getDocument().getTextLength()) { - moveOffset = 0; - } - myEditor.getCaretModel().moveToOffset(moveOffset); - myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); - } - - private static void assertIsDispatchThread() { - ApplicationManager.getApplication().assertIsDispatchThread(); - } - - public void setOutputPaused(final boolean value) { - myOutputPaused = value; - if (!value) { - requestFlushImmediately(); - } - } - - public boolean isOutputPaused() { - return myOutputPaused; - } - - public boolean hasDeferredOutput() { - synchronized (LOCK) { - return myDeferredOutput.length() > 0; - } - } - - public void performWhenNoDeferredOutput(final Runnable runnable) { - //Q: implement in another way without timer? - if (!hasDeferredOutput()) { - runnable.run(); - } - else { - mySpareTimeAlarm.addRequest( - new Runnable() { - public void run() { - performWhenNoDeferredOutput(runnable); - } - }, - 100 - ); - } - } - - public JComponent getComponent() { - if (myEditor == null) { - myEditor = createEditor(); - requestFlushImmediately(); - add(createCenterComponent(), BorderLayout.CENTER); - - myEditor.getDocument().addDocumentListener(new DocumentAdapter() { - public void documentChanged(DocumentEvent e) { - if (e.getNewLength() == 0 && e.getOffset() == 0) { - // string has been removed from the beginning, move tokens down - synchronized (LOCK) { - int toRemoveLen = e.getOldLength(); - int tIndex = findTokenInfoIndexByOffset(toRemoveLen); - ArrayList newTokens = new ArrayList(myTokens.subList(tIndex, myTokens.size())); - for (TokenInfo token : newTokens) { - token.startOffset -= toRemoveLen; - token.endOffset -= toRemoveLen; - } - if (!newTokens.isEmpty()) { - newTokens.get(0).startOffset = 0; - } - myContentSize -= Math.min(myContentSize, toRemoveLen); - myTokens = newTokens; - } - } - } - }); - } - return this; - } - - protected JComponent createCenterComponent() { - return myEditor.getComponent(); - } - - public void setModalityStateForUpdate(Computable stateComputable) { - myStateForUpdate = stateComputable; - } - - - public void dispose() { - myState = myState.dispose(); - if (myEditor != null) { - myFlushAlarm.cancelAllRequests(); - mySpareTimeAlarm.cancelAllRequests(); - disposeEditor(); - synchronized (LOCK) { - myDeferredOutput = new StringBuffer(); - } - myEditor = null; - } - } - - protected void disposeEditor() { - if (!myEditor.isDisposed()) { - EditorFactory.getInstance().releaseEditor(myEditor); - } - } - - public void print(String s, final ConsoleViewContentType contentType) { - synchronized (LOCK) { - myDeferredTypes.add(contentType); - - s = StringUtil.convertLineSeparators(s); - myContentSize += s.length(); - myDeferredOutput.append(s); - if (contentType == ConsoleViewContentType.USER_INPUT) { - myDeferredUserInput.append(s); - } - - boolean needNew = true; - if (!myTokens.isEmpty()) { - final TokenInfo lastToken = myTokens.get(myTokens.size() - 1); - if (lastToken.contentType == contentType) { - lastToken.endOffset = myContentSize; // optimization - needNew = false; - } - } - if (needNew) { - myTokens.add(new TokenInfo(contentType, myContentSize - s.length(), myContentSize)); - } - - if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0) { - if (contentType == ConsoleViewContentType.USER_INPUT) { - flushDeferredUserInput(); - } - } - if (myFlushAlarm.getActiveRequestCount() == 0 && myEditor != null) { - final boolean shouldFlushNow = USE_CYCLIC_BUFFER && myDeferredOutput.length() > CYCLIC_BUFFER_SIZE; - myFlushAlarm.addRequest(myFlushDeferredRunnable, shouldFlushNow ? 0 : FLUSH_DELAY, getStateForUpdate()); - } - } - } - - private ModalityState getStateForUpdate() { - return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); - } - - private void requestFlushImmediately() { - if (myEditor != null) { - myFlushAlarm.addRequest(myFlushDeferredRunnable, 0, getStateForUpdate()); - } - } - - public int getContentSize() { - return myContentSize; - } - - public boolean canPause() { - return true; - } - - private void flushDeferredText() { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (myProject.isDisposed()) { - return; - } - - final String text; - synchronized (LOCK) { - if (myOutputPaused) return; - if (myDeferredOutput.length() == 0) return; - if (myEditor == null) return; - - text = myDeferredOutput.substring(0, myDeferredOutput.length()); - if (USE_CYCLIC_BUFFER) { - myDeferredOutput = new StringBuffer(Math.min(myDeferredOutput.length(), CYCLIC_BUFFER_SIZE)); - } - else { - myDeferredOutput.setLength(0); - } - } - final Document document = myEditor.getDocument(); - final int oldLineCount = document.getLineCount(); - final boolean isAtEndOfDocument = myEditor.getCaretModel().getOffset() == document.getTextLength(); - boolean cycleUsed = USE_CYCLIC_BUFFER && document.getTextLength() + text.length() > CYCLIC_BUFFER_SIZE; - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - public void run() { - document.insertString(document.getTextLength(), text); - synchronized (LOCK) { - fireChange(); - } - } - }, null, DocCommandGroupId.noneGroupId(document)); - myPsiDisposedCheck.performCheck(); - final int newLineCount = document.getLineCount(); - if (cycleUsed) { - final int lineCount = LineTokenizer.calcLineCount(text, true); - for (Iterator it = myHyperlinks.getRanges().keySet().iterator(); it.hasNext();) { - if (!it.next().isValid()) { - it.remove(); - } - } - highlightHyperlinksAndFoldings(newLineCount >= lineCount + 1 ? newLineCount - lineCount - 1 : 0, newLineCount - 1); - } - else if (oldLineCount < newLineCount) { - highlightHyperlinksAndFoldings(oldLineCount - 1, newLineCount - 2); - } - - if (isAtEndOfDocument) { - scrollToTheEnd(); - } - } - - private void flushDeferredUserInput() { - final String text = myDeferredUserInput.substring(0, myDeferredUserInput.length()); - final int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r')); - if (index < 0) return; - final String textToSend = text.substring(0, index + 1); - myDeferredUserInput.setLength(0); - myDeferredUserInput.append(text.substring(index + 1)); - myFlushUserInputAlarm.addRequest(new Runnable() { - public void run() { - if (myState.isRunning()) { - try { - // this may block forever, see IDEA-54340 - myState.sendUserInput(textToSend); - } - catch (IOException ignored) { - } - } - } - }, 0); - } - - public Object getData(final String dataId) { - if (PlatformDataKeys.NAVIGATABLE.is(dataId)) { - if (myEditor == null) { - return null; - } - final LogicalPosition pos = myEditor.getCaretModel().getLogicalPosition(); - final HyperlinkInfo info = getHyperlinkInfoByLineAndCol(pos.line, pos.column); - final OpenFileDescriptor openFileDescriptor = info instanceof FileHyperlinkInfo ? ((FileHyperlinkInfo)info).getDescriptor() : null; - if (openFileDescriptor == null || !openFileDescriptor.getFile().isValid()) { - return null; - } - return openFileDescriptor; - } - - if (PlatformDataKeys.EDITOR.is(dataId)) { - return myEditor; - } - if (PlatformDataKeys.HELP_ID.is(dataId)) { - return myHelpId; - } - if (LangDataKeys.CONSOLE_VIEW.is(dataId)) { - return this; - } - return null; - } - - public void setHelpId(final String helpId) { - myHelpId = helpId; - } - - public void addMessageFilter(final Filter filter) { - myCustomFilter.addFilter(filter); - } - - public void printHyperlink(final String hyperlinkText, final HyperlinkInfo info) { - if (myEditor == null) return; - print(hyperlinkText, ConsoleViewContentType.NORMAL_OUTPUT); - flushDeferredText(); - final int textLength = myEditor.getDocument().getTextLength(); - addHyperlink(textLength - hyperlinkText.length(), textLength, null, info, getHyperlinkAttributes()); - } - - public static TextAttributes getHyperlinkAttributes() { - return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); - } - - public static TextAttributes getFollowedHyperlinkAttributes() { - return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.FOLLOWED_HYPERLINK_ATTRIBUTES); - } - - private Editor createEditor() { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public Editor compute() { - return doCreateEditor(); - } - }); - } - - private Editor doCreateEditor() { - final EditorEx editor = createRealEditor(); - editor.addEditorMouseListener(new EditorMouseAdapter() { - public void mouseReleased(final EditorMouseEvent e) { - final MouseEvent mouseEvent = e.getMouseEvent(); - if (!mouseEvent.isPopupTrigger()) { - navigate(e); - } - } - }); - - editor.addEditorMouseListener(new EditorPopupHandler() { - public void invokePopup(final EditorMouseEvent event) { - final MouseEvent mouseEvent = event.getMouseEvent(); - popupInvoked(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY()); - } - }); - - - final int bufferSize = USE_CYCLIC_BUFFER ? CYCLIC_BUFFER_SIZE : 0; - editor.getDocument().setCyclicBufferSize(bufferSize); - - editor.putUserData(CONSOLE_VIEW_IN_EDITOR_VIEW, this); - - editor.getContentComponent().addMouseMotionListener( - new MouseMotionAdapter() { - public void mouseMoved(final MouseEvent e) { - final HyperlinkInfo info = getHyperlinkInfoByPoint(e.getPoint()); - if (info != null) { - editor.getContentComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - else { - editor.getContentComponent().setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - } - } - } - ); - return editor; - } - - protected EditorEx createRealEditor() { - final EditorFactoryImpl document = (EditorFactoryImpl)EditorFactory.getInstance(); - final Document editorDocument = document.createDocument(true); - editorDocument.addDocumentListener(new DocumentListener() { - public void beforeDocumentChange(DocumentEvent event) { - } - - public void documentChanged(DocumentEvent event) { - if (myFileType != null) { - highlightUserTokens(); - } - } - }); - final EditorEx editor = (EditorEx)document.createViewer(editorDocument, myProject); - editor.getSettings().setAllowSingleLogicalLineFolding(true); // We want to fold long soft-wrapped command lines - editor.setSoftWrapAppliancePlace(SoftWrapAppliancePlaces.CONSOLE); - - final EditorHighlighter highlighter = createHighlighter(); - editor.setHighlighter(highlighter); - - final EditorSettings editorSettings = editor.getSettings(); - editorSettings.setLineMarkerAreaShown(false); - editorSettings.setIndentGuidesShown(false); - editorSettings.setLineNumbersShown(false); - editorSettings.setFoldingOutlineShown(true); - editorSettings.setAdditionalPageAtBottom(false); - editorSettings.setAdditionalColumnsCount(0); - editorSettings.setAdditionalLinesCount(0); - - final EditorColorsScheme scheme = editor.getColorsScheme(); - editor.setBackgroundColor(scheme.getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); - scheme.setColor(EditorColors.CARET_ROW_COLOR, null); - scheme.setColor(EditorColors.RIGHT_MARGIN_COLOR, null); - - final ConsoleViewImpl consoleView = this; - editor.getContentComponent().addKeyListener(new KeyListener() { - private int historyPosition = myHistory.size(); - - public void keyTyped(KeyEvent e) { - - } - - public void keyPressed(KeyEvent e) { - } - - public void keyReleased(KeyEvent e) { - if (e.isAltDown() && !e.isControlDown() && !e.isMetaDown() && !e.isShiftDown()) { - if (e.getKeyCode() == KeyEvent.VK_UP) { - historyPosition--; - if (historyPosition < 0) historyPosition = 0; - replaceString(); - e.consume(); - } - else if (e.getKeyCode() == KeyEvent.VK_DOWN) { - historyPosition++; - if (historyPosition > myHistory.size()) historyPosition = myHistory.size(); - replaceString(); - e.consume(); - } - } - else { - historyPosition = myHistory.size(); - } - } - - private void replaceString() { - final String str; - - if (myHistory.size() == historyPosition) { - str = ""; - } - else { - str = myHistory.get(historyPosition); - } - synchronized (LOCK) { - if (myTokens.isEmpty()) return; - final TokenInfo info = myTokens.get(myTokens.size() - 1); - if (info.contentType != ConsoleViewContentType.USER_INPUT) { - consoleView.insertUserText(str, 0); - } - else { - consoleView.replaceUserText(str, info.startOffset, info.endOffset); - } - } - } - }); - - setEditorUpActions(editor); - return editor; - } - - protected MyHighlighter createHighlighter() { - return new MyHighlighter(); - } - - private void highlightUserTokens() { - if (myTokens.isEmpty()) return; - final TokenInfo token = myTokens.get(myTokens.size() - 1); - if (token.contentType == ConsoleViewContentType.USER_INPUT) { - String text = myEditor.getDocument().getText().substring(token.startOffset, token.endOffset); - PsiFile file = PsiFileFactory.getInstance(myProject). - createFileFromText("dummy", myFileType, text, LocalTimeCounter.currentTime(), true); - Document document = PsiDocumentManager.getInstance(myProject).getDocument(file); - assert document != null; - Editor editor = EditorFactory.getInstance().createEditor(document, myProject, myFileType, false); - try { - RangeHighlighter[] allHighlighters = myEditor.getMarkupModel().getAllHighlighters(); - for (RangeHighlighter highlighter : allHighlighters) { - if (highlighter.getStartOffset() >= token.startOffset) { - myEditor.getMarkupModel().removeHighlighter(highlighter); - } - } - HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(0); - while (!iterator.atEnd()) { - myEditor.getMarkupModel() - .addRangeHighlighter(iterator.getStart() + token.startOffset, iterator.getEnd() + token.startOffset, HighlighterLayer.SYNTAX, - iterator.getTextAttributes(), - HighlighterTargetArea.EXACT_RANGE); - iterator.advance(); - } - } - finally { - EditorFactory.getInstance().releaseEditor(editor); - } - } - } - - private static void setEditorUpActions(final Editor editor) { - new EnterHandler().registerCustomShortcutSet(CommonShortcuts.ENTER, editor.getContentComponent()); - registerActionHandler(editor, IdeActions.ACTION_EDITOR_PASTE, new PasteHandler()); - registerActionHandler(editor, IdeActions.ACTION_EDITOR_BACKSPACE, new BackSpaceHandler()); - registerActionHandler(editor, IdeActions.ACTION_EDITOR_DELETE, new DeleteHandler()); - } - - private static void registerActionHandler(final Editor editor, final String actionId, final AnAction action) { - final Keymap keymap = KeymapManager.getInstance().getActiveKeymap(); - final Shortcut[] shortcuts = keymap.getShortcuts(actionId); - action.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), editor.getContentComponent()); - } - - private void popupInvoked(final Component component, final int x, final int y) { - final DefaultActionGroup group = new DefaultActionGroup(); - group.add(new ClearAllAction()); - group.add(new CopyAction()); - group.addSeparator(); - final ActionManager actionManager = ActionManager.getInstance(); - final ActionPopupMenu menu = actionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, (ActionGroup)actionManager.getAction(CONSOLE_VIEW_POPUP_MENU)); - menu.getComponent().show(component, x, y); - } - - private void navigate(final EditorMouseEvent event) { - if (event.getMouseEvent().isPopupTrigger()) return; - final Point p = event.getMouseEvent().getPoint(); - final HyperlinkInfo info = getHyperlinkInfoByPoint(p); - if (info != null) { - info.navigate(myProject); - linkFollowed(info); - } - } - - public static final Key OLD_HYPERLINK_TEXT_ATTRIBUTES = Key.create("OLD_HYPERLINK_TEXT_ATTRIBUTES"); - - private void linkFollowed(final HyperlinkInfo info) { - linkFollowed(myEditor, myHyperlinks, info); - } - - public static void linkFollowed(final Editor editor, final Hyperlinks hyperlinks, final HyperlinkInfo info) { - MarkupModelEx markupModel = (MarkupModelEx)editor.getMarkupModel(); - for (Map.Entry entry : hyperlinks.getRanges().entrySet()) { - RangeHighlighter range = entry.getKey(); - TextAttributes oldAttr = range.getUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES); - if (oldAttr != null) { - markupModel.setRangeHighlighterAttributes(range, oldAttr); - range.putUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES, null); - } - if (entry.getValue() == info) { - TextAttributes oldAttributes = range.getTextAttributes(); - range.putUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES, oldAttributes); - TextAttributes attributes = getFollowedHyperlinkAttributes().clone(); - assert oldAttributes != null; - attributes.setFontType(oldAttributes.getFontType()); - attributes.setEffectType(oldAttributes.getEffectType()); - attributes.setEffectColor(oldAttributes.getEffectColor()); - attributes.setForegroundColor(oldAttributes.getForegroundColor()); - markupModel.setRangeHighlighterAttributes(range, attributes); - } - } - //refresh highlighter text attributes - RangeHighlighter dummy = - markupModel.addRangeHighlighter(0, 0, HYPERLINK_LAYER, getHyperlinkAttributes(), HighlighterTargetArea.EXACT_RANGE); - markupModel.removeHighlighter(dummy); - } - - public HyperlinkInfo getHyperlinkInfoByPoint(final Point p) { - return getHyperlinkInfoByPoint(myEditor, myHyperlinks, p); - } - - public static HyperlinkInfo getHyperlinkInfoByPoint(final Editor editor, final Hyperlinks hyperlinks, final Point p) { - final LogicalPosition pos = editor.xyToLogicalPosition(new Point(p.x, p.y)); - return getHyperlinkInfoByLineAndCol(editor, hyperlinks, pos.line, pos.column); - } - - private HyperlinkInfo getHyperlinkInfoByLineAndCol(final int line, final int col) { - return getHyperlinkInfoByLineAndCol(myEditor, myHyperlinks, line, col); - } - - public static HyperlinkInfo getHyperlinkInfoByLineAndCol(final Editor editor, - final Hyperlinks hyperlinks, - final int line, - final int col) { - final int offset = editor.logicalPositionToOffset(new LogicalPosition(line, col)); - return hyperlinks.getHyperlinkAt(offset); - } - - private void highlightHyperlinksAndFoldings(final int line1, final int endLine) { - ApplicationManager.getApplication().assertIsDispatchThread(); - PsiDocumentManager.getInstance(myProject).commitAllDocuments(); - highlightHyperlinks(myEditor, myHyperlinks, myCustomFilter, myPredefinedMessageFilter, line1, endLine); - updateFoldings(line1, endLine, false); - } - - private void updateFoldings(final int line1, final int endLine, boolean immediately) { - final Document document = myEditor.getDocument(); - final CharSequence chars = document.getCharsSequence(); - final int startLine = Math.max(0, line1); - final List toAdd = new ArrayList(); - for (int line = startLine; line <= endLine; line++) { - addFolding(document, chars, line, toAdd); - } - if (!toAdd.isEmpty()) { - doUpdateFolding(toAdd, immediately); - } - } - - public static void highlightHyperlinks(final Editor editor, - final Hyperlinks hyperlinks, - final Filter myCustomFilter, - final Filter myPredefinedMessageFilter, - final int line1, final int endLine) { - final Document document = editor.getDocument(); - final TextAttributes hyperlinkAttributes = getHyperlinkAttributes(); - - final int startLine = Math.max(0, line1); - - for (int line = startLine; line <= endLine; line++) { - int endOffset = document.getLineEndOffset(line); - if (endOffset < document.getTextLength()) { - endOffset++; // add '\n' - } - final String text = getLineText(document, line, true); - Filter.Result result = myCustomFilter.applyFilter(text, endOffset); - if (result == null) { - result = myPredefinedMessageFilter.applyFilter(text, endOffset); - } - if (result != null) { - final int highlightStartOffset = result.highlightStartOffset; - final int highlightEndOffset = result.highlightEndOffset; - final HyperlinkInfo hyperlinkInfo = result.hyperlinkInfo; - addHyperlink(editor, hyperlinks, highlightStartOffset, highlightEndOffset, result.highlightAttributes, hyperlinkInfo, - hyperlinkAttributes); - } - } - } - - private void doUpdateFolding(final List toAdd, final boolean immediately) { - assertIsDispatchThread(); - myPendingFoldRegions.addAll(toAdd); - - myFoldingAlarm.cancelAllRequests(); - final Runnable runnable = new Runnable() { - public void run() { - assertIsDispatchThread(); - final FoldingModel model = myEditor.getFoldingModel(); - final Runnable operation = new Runnable() { - public void run() { - assertIsDispatchThread(); - for (FoldRegion region : myPendingFoldRegions) { - region.setExpanded(false); - model.addFoldRegion(region); - } - myPendingFoldRegions.clear(); - } - }; - if (immediately) { - model.runBatchFoldingOperation(operation); - } - else { - model.runBatchFoldingOperationDoNotCollapseCaret(operation); - } - } - }; - if (immediately || myPendingFoldRegions.size() > 100) { - runnable.run(); - } - else { - myFoldingAlarm.addRequest(runnable, 50); - } - } - - private void addFolding(Document document, CharSequence chars, int line, List toAdd) { - String commandLinePlaceholder = myCommandLineFolding.getPlaceholder(line); - if (commandLinePlaceholder != null) { - FoldRegion region = ((FoldingModelEx)myEditor.getFoldingModel()).createFoldRegion( - document.getLineStartOffset(line), document.getLineEndOffset(line), commandLinePlaceholder, null - ); - toAdd.add(region); - return; - } - ConsoleFolding current = foldingForLine(getLineText(document, line, false)); - if (current != null) { - myFolding.put(line, current); - } - - final ConsoleFolding prevFolding = myFolding.get(line - 1); - if (current == null && prevFolding != null) { - final int lEnd = line - 1; - int lStart = lEnd; - while (prevFolding.equals(myFolding.get(lStart - 1))) lStart--; - if (lStart == lEnd) { - return; - } - - for (int i = lStart; i <= lEnd; i++) { - myFolding.remove(i); - } - - List toFold = new ArrayList(lEnd - lStart + 1); - for (int i = lStart; i <= lEnd; i++) { - toFold.add(getLineText(document, i, false)); - } - - int oStart = document.getLineStartOffset(lStart); - if (oStart > 0) oStart--; - int oEnd = CharArrayUtil.shiftBackward(chars, document.getLineEndOffset(lEnd) - 1, " \t") + 1; - - FoldRegion region = - ((FoldingModelEx)myEditor.getFoldingModel()).createFoldRegion(oStart, oEnd, prevFolding.getPlaceholderText(toFold), null); - if (region != null) { - toAdd.add(region); - } - } - } - - public static String getLineText(Document document, int lineNumber, boolean includeEol) { - int endOffset = document.getLineEndOffset(lineNumber); - if (includeEol && endOffset < document.getTextLength()) { - endOffset++; - } - return document.getCharsSequence().subSequence(document.getLineStartOffset(lineNumber), endOffset).toString(); - } - - @Nullable - private static ConsoleFolding foldingForLine(String lineText) { - for (ConsoleFolding folding : ConsoleFolding.EP_NAME.getExtensions()) { - if (folding.shouldFoldLine(lineText)) { - return folding; - } - } - return null; - } - - private void addHyperlink(final int highlightStartOffset, - final int highlightEndOffset, - final TextAttributes highlightAttributes, - final HyperlinkInfo hyperlinkInfo, - final TextAttributes hyperlinkAttributes) { - addHyperlink(myEditor, myHyperlinks, highlightStartOffset, highlightEndOffset, highlightAttributes, hyperlinkInfo, hyperlinkAttributes); - } - - private static void addHyperlink(final Editor editor, - final Hyperlinks hyperlinks, - final int highlightStartOffset, - final int highlightEndOffset, - final TextAttributes highlightAttributes, - final HyperlinkInfo hyperlinkInfo, - final TextAttributes hyperlinkAttributes) { - TextAttributes textAttributes = highlightAttributes != null ? highlightAttributes : hyperlinkAttributes; - final RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(highlightStartOffset, - highlightEndOffset, - HYPERLINK_LAYER, - textAttributes, - HighlighterTargetArea.EXACT_RANGE); - hyperlinks.add(highlighter, hyperlinkInfo); - } - - public static class ClearAllAction extends DumbAwareAction { - public ClearAllAction() { - super(ExecutionBundle.message("clear.all.from.console.action.name")); - } - - @Override - public void update(AnActionEvent e) { - final boolean enabled = e.getData(LangDataKeys.CONSOLE_VIEW) != null; - e.getPresentation().setEnabled(enabled); - e.getPresentation().setVisible(enabled); - } - - public void actionPerformed(final AnActionEvent e) { - final ConsoleView consoleView = e.getData(LangDataKeys.CONSOLE_VIEW); - if (consoleView != null) { - consoleView.clear(); - } - } - } - - public static class CopyAction extends DumbAwareAction { - - @Override - public void update(AnActionEvent e) { - final Editor editor = e.getData(PlatformDataKeys.EDITOR); - final boolean enabled = editor != null && e.getData(LangDataKeys.CONSOLE_VIEW) != null; - e.getPresentation().setEnabled(enabled); - e.getPresentation().setVisible(enabled); - - e.getPresentation().setText(editor != null && editor.getSelectionModel().hasSelection() - ? ExecutionBundle.message("copy.selected.content.action.name") - : ExecutionBundle.message("copy.content.action.name")); - } - - public void actionPerformed(final AnActionEvent e) { - final Editor editor = e.getData(PlatformDataKeys.EDITOR); - assert editor != null; - if (editor.getSelectionModel().hasSelection()) { - editor.getSelectionModel().copySelectionToClipboard(); - } - else { - editor.getSelectionModel().setSelection(0, editor.getDocument().getTextLength()); - editor.getSelectionModel().copySelectionToClipboard(); - editor.getSelectionModel().removeSelection(); - } - } - } - - private class MyHighlighter extends DocumentAdapter implements EditorHighlighter { - private HighlighterClient myEditor; - - public HighlighterIterator createIterator(final int startOffset) { - final int startIndex = findTokenInfoIndexByOffset(startOffset); - - return new HighlighterIterator() { - private int myIndex = startIndex; - - public TextAttributes getTextAttributes() { - if (myFileType != null && getTokenInfo().contentType == ConsoleViewContentType.USER_INPUT) { - return ConsoleViewContentType.NORMAL_OUTPUT.getAttributes(); - } - return getTokenInfo() == null ? null : getTokenInfo().attributes; - } - - public int getStart() { - return getTokenInfo() == null ? 0 : getTokenInfo().startOffset; - } - - public int getEnd() { - return getTokenInfo() == null ? 0 : getTokenInfo().endOffset; - } - - public IElementType getTokenType() { - return null; - } - - public void advance() { - myIndex++; - } - - public void retreat() { - myIndex--; - } - - public boolean atEnd() { - return myIndex < 0 || myIndex >= myTokens.size(); - } - - public Document getDocument() { - return myEditor.getDocument(); - } - - private TokenInfo getTokenInfo() { - return myTokens.get(myIndex); - } - }; - } - - public void setText(final CharSequence text) { - } - - public void setEditor(final HighlighterClient editor) { - LOG.assertTrue(myEditor == null, "Highlighters cannot be reused with different editors"); - myEditor = editor; - } - - public void setColorScheme(EditorColorsScheme scheme) { - } - } - - private int findTokenInfoIndexByOffset(final int offset) { - int low = 0; - int high = myTokens.size() - 1; - - while (low <= high) { - final int mid = (low + high) / 2; - final TokenInfo midVal = myTokens.get(mid); - if (offset < midVal.startOffset) { - high = mid - 1; - } - else if (offset >= midVal.endOffset) { - low = mid + 1; - } - else { - return mid; - } - } - return myTokens.size(); - } - - private static class MyTypedHandler implements TypedActionHandler { - private final TypedActionHandler myOriginalHandler; - - private MyTypedHandler(final TypedActionHandler originalAction) { - myOriginalHandler = originalAction; - } - - public void execute(@NotNull final Editor editor, final char charTyped, @NotNull final DataContext dataContext) { - final ConsoleViewImpl consoleView = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW); - if (consoleView == null || !consoleView.myState.isRunning() || consoleView.isViewer) { - myOriginalHandler.execute(editor, charTyped, dataContext); - } - else { - final String s = String.valueOf(charTyped); - SelectionModel selectionModel = editor.getSelectionModel(); - if (selectionModel.hasSelection()) { - consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); - } - else { - consoleView.insertUserText(s, editor.getCaretModel().getOffset()); - } - } - } - } - - private abstract static class ConsoleAction extends AnAction implements DumbAware { - public void actionPerformed(final AnActionEvent e) { - final DataContext context = e.getDataContext(); - final ConsoleViewImpl console = getRunningConsole(context); - execute(console, context); - } - - protected abstract void execute(ConsoleViewImpl console, final DataContext context); - - public void update(final AnActionEvent e) { - final ConsoleViewImpl console = getRunningConsole(e.getDataContext()); - e.getPresentation().setEnabled(console != null); - } - - @Nullable - private static ConsoleViewImpl getRunningConsole(final DataContext context) { - final Editor editor = PlatformDataKeys.EDITOR.getData(context); - if (editor != null) { - final ConsoleViewImpl console = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW); - if (console != null && console.myState.isRunning()) { - return console; - } - } - return null; - } - } - - private static class EnterHandler extends ConsoleAction { - public void execute(final ConsoleViewImpl consoleView, final DataContext context) { - synchronized (consoleView.LOCK) { - String str = consoleView.myDeferredUserInput.toString(); - if (StringUtil.isNotEmpty(str)) { - consoleView.myHistory.remove(str); - consoleView.myHistory.add(str); - if (consoleView.myHistory.size() > consoleView.myHistorySize) consoleView.myHistory.remove(0); - } - for (ConsoleInputListener listener : consoleView.myConsoleInputListeners) { - listener.textEntered(str); - } - } - consoleView.print("\n", ConsoleViewContentType.USER_INPUT); - consoleView.flushDeferredText(); - final Editor editor = consoleView.myEditor; - editor.getCaretModel().moveToOffset(editor.getDocument().getTextLength()); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - } - } - - private static class PasteHandler extends ConsoleAction { - public void execute(final ConsoleViewImpl consoleView, final DataContext context) { - final Transferable content = CopyPasteManager.getInstance().getContents(); - if (content == null) return; - String s = null; - try { - s = (String)content.getTransferData(DataFlavor.stringFlavor); - } - catch (Exception e) { - consoleView.getToolkit().beep(); - } - if (s == null) return; - Editor editor = consoleView.myEditor; - SelectionModel selectionModel = editor.getSelectionModel(); - if (selectionModel.hasSelection()) { - consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); - } - else { - consoleView.insertUserText(s, editor.getCaretModel().getOffset()); - } - } - } - - private static class BackSpaceHandler extends ConsoleAction { - public void execute(final ConsoleViewImpl consoleView, final DataContext context) { - final Editor editor = consoleView.myEditor; - - if (IncrementalSearchHandler.isHintVisible(editor)) { - getDefaultActionHandler().execute(editor, context); - return; - } - - final Document document = editor.getDocument(); - final int length = document.getTextLength(); - if (length == 0) { - return; - } - - SelectionModel selectionModel = editor.getSelectionModel(); - if (selectionModel.hasSelection()) { - consoleView.deleteUserText(selectionModel.getSelectionStart(), - selectionModel.getSelectionEnd() - selectionModel.getSelectionStart()); - } - else if (editor.getCaretModel().getOffset() > 0) { - consoleView.deleteUserText(editor.getCaretModel().getOffset() - 1, 1); - } - } - - private static EditorActionHandler getDefaultActionHandler() { - return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); - } - } - - private static class DeleteHandler extends ConsoleAction { - public void execute(final ConsoleViewImpl consoleView, final DataContext context) { - final Editor editor = consoleView.myEditor; - - if (IncrementalSearchHandler.isHintVisible(editor)) { - getDefaultActionHandler().execute(editor, context); - return; - } - - final Document document = editor.getDocument(); - final int length = document.getTextLength(); - if (length == 0) { - return; - } - - SelectionModel selectionModel = editor.getSelectionModel(); - if (selectionModel.hasSelection()) { - consoleView.deleteUserText(selectionModel.getSelectionStart(), - selectionModel.getSelectionEnd() - selectionModel.getSelectionStart()); - } - else { - consoleView.deleteUserText(editor.getCaretModel().getOffset(), 1); - } - } - - private static EditorActionHandler getDefaultActionHandler() { - return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); - } - } - - public static class Hyperlinks { - private static final int NO_INDEX = Integer.MIN_VALUE; - private final Map myHighlighterToMessageInfoMap = new HashMap(); - private int myLastIndex = NO_INDEX; - - public void clear() { - myHighlighterToMessageInfoMap.clear(); - myLastIndex = NO_INDEX; - } - - public HyperlinkInfo getHyperlinkAt(final int offset) { - for (final RangeHighlighter highlighter : myHighlighterToMessageInfoMap.keySet()) { - if (highlighter.isValid() && containsOffset(offset, highlighter)) { - return myHighlighterToMessageInfoMap.get(highlighter); - } - } - return null; - } - - private static boolean containsOffset(final int offset, final RangeHighlighter highlighter) { - return highlighter.getStartOffset() <= offset && offset <= highlighter.getEndOffset(); - } - - public void add(final RangeHighlighter highlighter, final HyperlinkInfo hyperlinkInfo) { - myHighlighterToMessageInfoMap.put(highlighter, hyperlinkInfo); - if (myLastIndex != NO_INDEX && containsOffset(myLastIndex, highlighter)) myLastIndex = NO_INDEX; - } - - public Map getRanges() { - return myHighlighterToMessageInfoMap; - } - } - - public JComponent getPreferredFocusableComponent() { - //ensure editor created - getComponent(); - return myEditor.getContentComponent(); - } - - - // navigate up/down in stack trace - public boolean hasNextOccurence() { - return next(1, false) != null; - } - - public boolean hasPreviousOccurence() { - return next(-1, false) != null; - } - - public OccurenceInfo goNextOccurence() { - return next(1, true); - } - - @Nullable - private OccurenceInfo next(final int delta, boolean doMove) { - List ranges = new ArrayList(myHyperlinks.getRanges().keySet()); - for (Iterator iterator = ranges.iterator(); iterator.hasNext();) { - RangeHighlighter highlighter = iterator.next(); - if (myEditor.getFoldingModel().getCollapsedRegionAtOffset(highlighter.getStartOffset()) != null) { - iterator.remove(); - } - } - Collections.sort(ranges, new Comparator() { - public int compare(final RangeHighlighter o1, final RangeHighlighter o2) { - return o1.getStartOffset() - o2.getStartOffset(); - } - }); - int i; - for (i = 0; i < ranges.size(); i++) { - RangeHighlighter range = ranges.get(i); - if (range.getUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES) != null) { - break; - } - } - int newIndex = ranges.isEmpty() ? -1 : i == ranges.size() ? 0 : (i + delta + ranges.size()) % ranges.size(); - RangeHighlighter next = newIndex < ranges.size() && newIndex >= 0 ? ranges.get(newIndex) : null; - if (next == null) return null; - if (doMove) { - scrollTo(next.getStartOffset()); - } - final HyperlinkInfo hyperlinkInfo = myHyperlinks.getRanges().get(next); - return hyperlinkInfo == null ? null : new OccurenceInfo(new Navigatable.Adapter() { - public void navigate(final boolean requestFocus) { - hyperlinkInfo.navigate(myProject); - linkFollowed(hyperlinkInfo); - } - }, i, ranges.size()); - } - - public OccurenceInfo goPreviousOccurence() { - return next(-1, true); - } - - public String getNextOccurenceActionName() { - return ExecutionBundle.message("down.the.stack.trace"); - } - - public String getPreviousOccurenceActionName() { - return ExecutionBundle.message("up.the.stack.trace"); - } - - public void addCustomConsoleAction(@NotNull AnAction action) { - customActions.add(action); - } - - @NotNull - public AnAction[] createConsoleActions() { - //Initializing prev and next occurrences actions - final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); - final AnAction prevAction = actionsManager.createPrevOccurenceAction(this); - prevAction.getTemplatePresentation().setText(getPreviousOccurenceActionName()); - final AnAction nextAction = actionsManager.createNextOccurenceAction(this); - nextAction.getTemplatePresentation().setText(getNextOccurenceActionName()); - - final AnAction switchSoftWrapsAction = new ToggleUseSoftWrapsToolbarAction(SoftWrapAppliancePlaces.CONSOLE) { - @Override - protected Editor getEditor(AnActionEvent e) { - return myEditor; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - EditorSettingsExternalizable.getInstance().setUseSoftWraps(myEditor.getSettings().isUseSoftWraps(), SoftWrapAppliancePlaces.CONSOLE); - } - }; - final AnAction autoScrollToTheEndAction = new ScrollToTheEndToolbarAction() { - @Override - public void actionPerformed(final AnActionEvent e) { - scrollToTheEnd(); - } - }; - - //Initializing custom actions - final AnAction[] consoleActions = new AnAction[4 + customActions.size()]; - consoleActions[0] = prevAction; - consoleActions[1] = nextAction; - consoleActions[2] = switchSoftWrapsAction; - consoleActions[3] = autoScrollToTheEndAction; - for (int i = 0; i < customActions.size(); ++i) { - consoleActions[i + 4] = customActions.get(i); - } - return consoleActions; - } - - protected void scrollToTheEnd() { - myEditor.getCaretModel().moveToOffset(myEditor.getDocument().getTextLength()); - myEditor.getSelectionModel().removeSelection(); - myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - } - - public void setEditorEnabled(boolean enabled) { - myEditor.getContentComponent().setEnabled(enabled); - } - - private void fireChange() { - if (myDeferredTypes.isEmpty()) return; - Collection types = Collections.unmodifiableCollection(myDeferredTypes); - - for (ChangeListener each : myListeners) { - each.contentAdded(types); - } - - myDeferredTypes.clear(); - } - - public void addChangeListener(final ChangeListener listener, final Disposable parent) { - myListeners.add(listener); - Disposer.register(parent, new Disposable() { - public void dispose() { - myListeners.remove(listener); - } - }); - } - - /** - * insert text to document - * - * @param s inserted text - * @param offset relatively to all document text - */ - private void insertUserText(final String s, int offset) { - final ConsoleViewImpl consoleView = this; - final Editor editor = consoleView.myEditor; - final Document document = editor.getDocument(); - final int startOffset; - - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) return; - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); - if (info.contentType != ConsoleViewContentType.USER_INPUT && !s.contains("\n")) { - consoleView.print(s, ConsoleViewContentType.USER_INPUT); - consoleView.flushDeferredText(); - editor.getCaretModel().moveToOffset(document.getTextLength()); - editor.getSelectionModel().removeSelection(); - return; - } - else if (info.contentType != ConsoleViewContentType.USER_INPUT) { - insertUserText("temp", offset); - final TokenInfo newInfo = consoleView.myTokens.get(consoleView.myTokens.size() - 1); - replaceUserText(s, newInfo.startOffset, newInfo.endOffset); - return; - } - - final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); - if (offset > info.endOffset) { - startOffset = info.endOffset; - } - else { - startOffset = Math.max(deferredOffset, Math.max(info.startOffset, offset)); - } - - consoleView.myDeferredUserInput.insert(startOffset - deferredOffset, s); - - int charCountToAdd = s.length(); - info.endOffset += charCountToAdd; - consoleView.myContentSize += charCountToAdd; - } - - document.insertString(startOffset, s); - // Math.max is needed when cyclic buffer is used - editor.getCaretModel().moveToOffset(Math.min(startOffset + s.length(), document.getTextLength())); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - } - - /** - * replace text - * - * @param s text for replace - * @param start relativly to all document text - * @param end relativly to all document text - */ - private void replaceUserText(final String s, int start, int end) { - if (start == end) { - insertUserText(s, start); - return; - } - final ConsoleViewImpl consoleView = this; - final Editor editor = consoleView.myEditor; - final Document document = editor.getDocument(); - final int startOffset; - final int endOffset; - - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) return; - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); - if (info.contentType != ConsoleViewContentType.USER_INPUT) { - consoleView.print(s, ConsoleViewContentType.USER_INPUT); - consoleView.flushDeferredText(); - editor.getCaretModel().moveToOffset(document.getTextLength()); - editor.getSelectionModel().removeSelection(); - return; - } - if (consoleView.myDeferredUserInput.length() == 0) return; - - final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); - - startOffset = getStartOffset(start, info, deferredOffset); - endOffset = getEndOffset(end, info); - - if (startOffset == -1 || - endOffset == -1 || - endOffset <= startOffset) { - editor.getSelectionModel().removeSelection(); - editor.getCaretModel().moveToOffset(start); - return; - } - int charCountToReplace = s.length() - endOffset + startOffset; - - consoleView.myDeferredUserInput.replace(startOffset - deferredOffset, endOffset - deferredOffset, s); - - info.endOffset += charCountToReplace; - if (info.startOffset == info.endOffset) { - consoleView.myTokens.remove(consoleView.myTokens.size() - 1); - } - consoleView.myContentSize += charCountToReplace; - } - - document.replaceString(startOffset, endOffset, s); - editor.getCaretModel().moveToOffset(startOffset + s.length()); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - editor.getSelectionModel().removeSelection(); - } - - /** - * delete text - * - * @param offset relativly to all document text - * @param length lenght of deleted text - */ - private void deleteUserText(int offset, int length) { - ConsoleViewImpl consoleView = this; - final Editor editor = consoleView.myEditor; - final Document document = editor.getDocument(); - final int startOffset; - final int endOffset; - - synchronized (consoleView.LOCK) { - if (consoleView.myTokens.isEmpty()) return; - final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); - if (info.contentType != ConsoleViewContentType.USER_INPUT) return; - if (consoleView.myDeferredUserInput.length() == 0) return; - - final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); - startOffset = getStartOffset(offset, info, deferredOffset); - endOffset = getEndOffset(offset + length, info); - if (startOffset == -1 || - endOffset == -1 || - endOffset <= startOffset || - startOffset < deferredOffset) { - editor.getSelectionModel().removeSelection(); - editor.getCaretModel().moveToOffset(offset); - return; - } - - consoleView.myDeferredUserInput.delete(startOffset - deferredOffset, endOffset - deferredOffset); - int charCountToDelete = endOffset - startOffset; - - info.endOffset -= charCountToDelete; - if (info.startOffset == info.endOffset) { - consoleView.myTokens.remove(consoleView.myTokens.size() - 1); - } - consoleView.myContentSize -= charCountToDelete; - } - - document.deleteString(startOffset, endOffset); - editor.getCaretModel().moveToOffset(startOffset); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - editor.getSelectionModel().removeSelection(); - } - - //util methods for add, replace, delete methods - private static int getStartOffset(int offset, TokenInfo info, int deferredOffset) { - int startOffset; - if (offset >= info.startOffset && offset < info.endOffset) { - startOffset = Math.max(offset, deferredOffset); - } - else if (offset < info.startOffset) { - startOffset = Math.max(info.startOffset, deferredOffset); - } - else { - startOffset = -1; - } - return startOffset; - } - - private static int getEndOffset(int offset, TokenInfo info) { - int endOffset; - if (offset > info.endOffset) { - endOffset = info.endOffset; - } - else if (offset <= info.startOffset) { - endOffset = -1; - } - else { - endOffset = offset; - } - return endOffset; - } - - /** - * Command line used to launch application/test from idea is quite a big most of the time (it's likely that classpath definition - * takes a lot of space). Hence, it takes many visual lines during representation if soft wraps are enabled. - *

- * Our point is to fold such long command line and represent it as a single visual line by default. - */ - private class CommandLineFolding extends ConsoleFolding { - - /** - * Checks if target line should be folded and returns its placeholder if the examination succeeds. - * - * @param line index of line to check - * @return placeholder text if given line should be folded; null otherwise - */ - @Nullable - public String getPlaceholder(int line) { - if (myEditor == null || line != 0 || !myEditor.getSettings().isUseSoftWraps()) { - return null; - } - - String text = getLineText(myEditor.getDocument(), line, false); - if (text.length() < 1000) { - return null; - } - // Don't fold the first line if no soft wraps are used or if the line is not that big. - if (!myEditor.getSettings().isUseSoftWraps() || text.length() < 1000) { - return null; - } - boolean nonWhiteSpaceFound = false; - int index = 0; - for (; index < text.length(); index++) { - char c = text.charAt(index); - if (c != ' ' && c != '\t') { - nonWhiteSpaceFound = true; - continue; - } - if (nonWhiteSpaceFound) { - break; - } - } - if (index > text.length()) { - // Don't expect to be here - return "<...>"; - } - return text.substring(0, index) + " ..."; - } - - @Override - public boolean shouldFoldLine(String line) { - return false; - } - - @Override - public String getPlaceholderText(List lines) { - // Is not expected to be called. - return "<...>"; - } - } -} - +/* + * Copyright 2000-2009 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.impl; + +import com.intellij.codeInsight.navigation.IncrementalSearchHandler; +import com.intellij.execution.ConsoleFolding; +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.filters.*; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.ui.ConsoleView; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.execution.ui.ObservableConsoleView; +import com.intellij.ide.CommonActionsManager; +import com.intellij.ide.OccurenceNavigator; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.actionSystem.*; +import com.intellij.openapi.editor.actions.ScrollToTheEndToolbarAction; +import com.intellij.openapi.editor.actions.ToggleUseSoftWrapsToolbarAction; +import com.intellij.openapi.editor.colors.CodeInsightColors; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.event.*; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.editor.ex.FoldingModelEx; +import com.intellij.openapi.editor.ex.MarkupModelEx; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.editor.highlighter.HighlighterClient; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.editor.impl.EditorFactoryImpl; +import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; +import com.intellij.openapi.editor.markup.HighlighterLayer; +import com.intellij.openapi.editor.markup.HighlighterTargetArea; +import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.keymap.Keymap; +import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.LineTokenizer; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.Alarm; +import com.intellij.util.EditorPopupHandler; +import com.intellij.util.LocalTimeCounter; +import com.intellij.util.containers.HashMap; +import com.intellij.util.text.CharArrayUtil; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.io.IOException; +import java.util.*; +import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; + +public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { + private @NonNls String CONSOLE_VIEW_POPUP_MENU = "ConsoleView.PopupMenu"; + private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.ConsoleViewImpl"); + + private static final int FLUSH_DELAY = 200; //TODO : make it an option + + private static final Key CONSOLE_VIEW_IN_EDITOR_VIEW = Key.create("CONSOLE_VIEW_IN_EDITOR_VIEW"); + + static { + final EditorActionManager actionManager = EditorActionManager.getInstance(); + final TypedAction typedAction = actionManager.getTypedAction(); + typedAction.setupHandler(new MyTypedHandler(typedAction.getHandler())); + } + + private final int CYCLIC_BUFFER_SIZE = getCycleBufferSize(); + private final CommandLineFolding myCommandLineFolding = new CommandLineFolding(); + + private final DisposedPsiManagerCheck myPsiDisposedCheck; + private final boolean isViewer; + + private ConsoleState myState = ConsoleState.NOT_STARTED; + private Computable myStateForUpdate; + + private static int getCycleBufferSize() { + final String cycleBufferSizeProperty = System.getProperty("idea.cycle.buffer.size"); + if (cycleBufferSizeProperty == null) return 1024 * 1024; + try { + return Integer.parseInt(cycleBufferSizeProperty) * 1024; + } + catch (NumberFormatException e) { + return 1024 * 1024; + } + } + + private final boolean USE_CYCLIC_BUFFER = useCycleBuffer(); + + private static boolean useCycleBuffer() { + final String useCycleBufferProperty = System.getProperty("idea.cycle.buffer.size"); + return useCycleBufferProperty == null || !"disabled".equalsIgnoreCase(useCycleBufferProperty); + } + + private static final int HYPERLINK_LAYER = HighlighterLayer.SELECTION - 123; + private final Alarm mySpareTimeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); + + private final CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); + private final Set myDeferredTypes = new HashSet(); + private final ArrayList customActions = new ArrayList(); + + @TestOnly + public Editor getEditor() { + return myEditor; + } + + public void scrollToEnd() { + if (myEditor == null) return; + myEditor.getCaretModel().moveToOffset(myEditor.getDocument().getTextLength()); + } + + public void foldImmediately() { + ApplicationManager.getApplication().assertIsDispatchThread(); + if (myFlushAlarm.getActiveRequestCount() > 0) { + myFlushAlarm.cancelAllRequests(); + myFlushDeferredRunnable.run(); + } + + myFoldingAlarm.cancelAllRequests(); + + myPendingFoldRegions.clear(); + final FoldingModel model = myEditor.getFoldingModel(); + model.runBatchFoldingOperation(new Runnable() { + public void run() { + for (FoldRegion region : model.getAllFoldRegions()) { + model.removeFoldRegion(region); + } + } + }); + myFolding.clear(); + + updateFoldings(0, myEditor.getDocument().getLineCount() - 1, true); + } + + private static class TokenInfo { + private final ConsoleViewContentType contentType; + private int startOffset; + private int endOffset; + private final TextAttributes attributes; + + private TokenInfo(final ConsoleViewContentType contentType, final int startOffset, final int endOffset) { + this.contentType = contentType; + this.startOffset = startOffset; + this.endOffset = endOffset; + attributes = contentType.getAttributes(); + } + } + + private final Project myProject; + + private boolean myOutputPaused; + + private Editor myEditor; + + private final Object LOCK = new Object(); + + private int myContentSize; + private StringBuffer myDeferredOutput = new StringBuffer(); + private StringBuffer myDeferredUserInput = new StringBuffer(); + + private ArrayList myTokens = new ArrayList(); + private final Hyperlinks myHyperlinks = new Hyperlinks(); + private final TIntObjectHashMap myFolding = new TIntObjectHashMap(); + + private String myHelpId; + + private final Alarm myFlushUserInputAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, this); + private final Alarm myFlushAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); + private final Runnable myFlushDeferredRunnable = new Runnable() { + public void run() { + flushDeferredText(); + } + }; + + protected final CompositeFilter myPredefinedMessageFilter; + protected final CompositeFilter myCustomFilter; + + private final ArrayList myHistory = new ArrayList(); + private int myHistorySize = 20; + + private final ArrayList myConsoleInputListeners = new ArrayList(); + + private final Alarm myFoldingAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); + private final List myPendingFoldRegions = new ArrayList(); + + public void addConsoleUserInputListener(ConsoleInputListener consoleInputListener) { + myConsoleInputListeners.add(consoleInputListener); + } + + /** + * By default history works for one session. If + * you want to import previous session, set it up here. + * + * @param history where you can save history + */ + public void importHistory(Collection history) { + myHistory.clear(); + myHistory.addAll(history); + while (myHistory.size() > myHistorySize) { + myHistory.remove(0); + } + } + + public List getHistory() { + return Collections.unmodifiableList(myHistory); + } + + public void setHistorySize(int historySize) { + myHistorySize = historySize; + } + + public int getHistorySize() { + return myHistorySize; + } + + private FileType myFileType; + + /** + * Use it for custom highlighting for user text. + * This will be highlighted as appropriate file to this file type. + * + * @param fileType according to which use highlighting + */ + public void setFileType(FileType fileType) { + myFileType = fileType; + } + + public ConsoleViewImpl(final Project project, boolean viewer) { + this(project, viewer, null); + } + + public ConsoleViewImpl(final Project project, boolean viewer, FileType fileType) { + this(project, GlobalSearchScope.allScope(project), viewer, fileType); + } + + + public ConsoleViewImpl(final Project project, GlobalSearchScope searchScope, boolean viewer, FileType fileType) { + super(new BorderLayout()); + isViewer = viewer; + myPsiDisposedCheck = new DisposedPsiManagerCheck(project); + myProject = project; + myFileType = fileType; + + myCustomFilter = new CompositeFilter(project); + myPredefinedMessageFilter = new CompositeFilter(project); + for (ConsoleFilterProvider eachProvider : Extensions.getExtensions(ConsoleFilterProvider.FILTER_PROVIDERS)) { + Filter[] filters = eachProvider instanceof ConsoleFilterProviderEx + ? ((ConsoleFilterProviderEx)eachProvider).getDefaultFilters(project, searchScope) + : eachProvider.getDefaultFilters(project); + for (Filter filter : filters) { + myPredefinedMessageFilter.addFilter(filter); + } + } + + Disposer.register(project, this); + } + + public void attachToProcess(final ProcessHandler processHandler) { + myState = myState.attachTo(this, processHandler); + } + + public void clear() { + assertIsDispatchThread(); + + final Document document; + synchronized (LOCK) { + myContentSize = 0; + if (USE_CYCLIC_BUFFER) { + myDeferredOutput = new StringBuffer(Math.min(myDeferredOutput.length(), CYCLIC_BUFFER_SIZE)); + } + else { + myDeferredOutput = new StringBuffer(); + } + myDeferredTypes.clear(); + myDeferredUserInput = new StringBuffer(); + myHyperlinks.clear(); + myTokens.clear(); + if (myEditor == null) return; + myEditor.getMarkupModel().removeAllHighlighters(); + document = myEditor.getDocument(); + myFoldingAlarm.cancelAllRequests(); + } + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + document.deleteString(0, document.getTextLength()); + } + }, null, DocCommandGroupId.noneGroupId(document)); + } + + public void scrollTo(final int offset) { + assertIsDispatchThread(); + flushDeferredText(); + if (myEditor == null) return; + int moveOffset = offset; + if (USE_CYCLIC_BUFFER && moveOffset >= myEditor.getDocument().getTextLength()) { + moveOffset = 0; + } + myEditor.getCaretModel().moveToOffset(moveOffset); + myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); + } + + private static void assertIsDispatchThread() { + ApplicationManager.getApplication().assertIsDispatchThread(); + } + + public void setOutputPaused(final boolean value) { + myOutputPaused = value; + if (!value) { + requestFlushImmediately(); + } + } + + public boolean isOutputPaused() { + return myOutputPaused; + } + + public boolean hasDeferredOutput() { + synchronized (LOCK) { + return myDeferredOutput.length() > 0; + } + } + + public void performWhenNoDeferredOutput(final Runnable runnable) { + //Q: implement in another way without timer? + if (!hasDeferredOutput()) { + runnable.run(); + } + else { + mySpareTimeAlarm.addRequest( + new Runnable() { + public void run() { + performWhenNoDeferredOutput(runnable); + } + }, + 100 + ); + } + } + + public JComponent getComponent() { + if (myEditor == null) { + myEditor = createEditor(); + requestFlushImmediately(); + add(createCenterComponent(), BorderLayout.CENTER); + + myEditor.getDocument().addDocumentListener(new DocumentAdapter() { + public void documentChanged(DocumentEvent e) { + if (e.getNewLength() == 0 && e.getOffset() == 0) { + // string has been removed from the beginning, move tokens down + synchronized (LOCK) { + int toRemoveLen = e.getOldLength(); + int tIndex = findTokenInfoIndexByOffset(toRemoveLen); + ArrayList newTokens = new ArrayList(myTokens.subList(tIndex, myTokens.size())); + for (TokenInfo token : newTokens) { + token.startOffset -= toRemoveLen; + token.endOffset -= toRemoveLen; + } + if (!newTokens.isEmpty()) { + newTokens.get(0).startOffset = 0; + } + myContentSize -= Math.min(myContentSize, toRemoveLen); + myTokens = newTokens; + } + } + } + }); + } + return this; + } + + protected JComponent createCenterComponent() { + return myEditor.getComponent(); + } + + public void setModalityStateForUpdate(Computable stateComputable) { + myStateForUpdate = stateComputable; + } + + + public void dispose() { + myState = myState.dispose(); + if (myEditor != null) { + myFlushAlarm.cancelAllRequests(); + mySpareTimeAlarm.cancelAllRequests(); + disposeEditor(); + synchronized (LOCK) { + myDeferredOutput = new StringBuffer(); + } + myEditor = null; + } + } + + protected void disposeEditor() { + if (!myEditor.isDisposed()) { + EditorFactory.getInstance().releaseEditor(myEditor); + } + } + + public void print(String s, final ConsoleViewContentType contentType) { + synchronized (LOCK) { + myDeferredTypes.add(contentType); + + s = StringUtil.convertLineSeparators(s); + myContentSize += s.length(); + myDeferredOutput.append(s); + if (contentType == ConsoleViewContentType.USER_INPUT) { + myDeferredUserInput.append(s); + } + + boolean needNew = true; + if (!myTokens.isEmpty()) { + final TokenInfo lastToken = myTokens.get(myTokens.size() - 1); + if (lastToken.contentType == contentType) { + lastToken.endOffset = myContentSize; // optimization + needNew = false; + } + } + if (needNew) { + myTokens.add(new TokenInfo(contentType, myContentSize - s.length(), myContentSize)); + } + + if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0) { + if (contentType == ConsoleViewContentType.USER_INPUT) { + flushDeferredUserInput(); + } + } + if (myFlushAlarm.getActiveRequestCount() == 0 && myEditor != null) { + final boolean shouldFlushNow = USE_CYCLIC_BUFFER && myDeferredOutput.length() > CYCLIC_BUFFER_SIZE; + myFlushAlarm.addRequest(myFlushDeferredRunnable, shouldFlushNow ? 0 : FLUSH_DELAY, getStateForUpdate()); + } + } + } + + private ModalityState getStateForUpdate() { + return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); + } + + private void requestFlushImmediately() { + if (myEditor != null) { + myFlushAlarm.addRequest(myFlushDeferredRunnable, 0, getStateForUpdate()); + } + } + + public int getContentSize() { + return myContentSize; + } + + public boolean canPause() { + return true; + } + + private void flushDeferredText() { + ApplicationManager.getApplication().assertIsDispatchThread(); + if (myProject.isDisposed()) { + return; + } + + final String text; + synchronized (LOCK) { + if (myOutputPaused) return; + if (myDeferredOutput.length() == 0) return; + if (myEditor == null) return; + + text = myDeferredOutput.substring(0, myDeferredOutput.length()); + if (USE_CYCLIC_BUFFER) { + myDeferredOutput = new StringBuffer(Math.min(myDeferredOutput.length(), CYCLIC_BUFFER_SIZE)); + } + else { + myDeferredOutput.setLength(0); + } + } + final Document document = myEditor.getDocument(); + final int oldLineCount = document.getLineCount(); + final boolean isAtEndOfDocument = myEditor.getCaretModel().getOffset() == document.getTextLength(); + boolean cycleUsed = USE_CYCLIC_BUFFER && document.getTextLength() + text.length() > CYCLIC_BUFFER_SIZE; + CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + public void run() { + document.insertString(document.getTextLength(), text); + synchronized (LOCK) { + fireChange(); + } + } + }, null, DocCommandGroupId.noneGroupId(document)); + myPsiDisposedCheck.performCheck(); + final int newLineCount = document.getLineCount(); + if (cycleUsed) { + final int lineCount = LineTokenizer.calcLineCount(text, true); + for (Iterator it = myHyperlinks.getRanges().keySet().iterator(); it.hasNext();) { + if (!it.next().isValid()) { + it.remove(); + } + } + highlightHyperlinksAndFoldings(newLineCount >= lineCount + 1 ? newLineCount - lineCount - 1 : 0, newLineCount - 1); + } + else if (oldLineCount < newLineCount) { + highlightHyperlinksAndFoldings(oldLineCount - 1, newLineCount - 2); + } + + if (isAtEndOfDocument) { + scrollToTheEnd(); + } + } + + private void flushDeferredUserInput() { + final String text = myDeferredUserInput.substring(0, myDeferredUserInput.length()); + final int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r')); + if (index < 0) return; + final String textToSend = text.substring(0, index + 1); + myDeferredUserInput.setLength(0); + myDeferredUserInput.append(text.substring(index + 1)); + myFlushUserInputAlarm.addRequest(new Runnable() { + public void run() { + if (myState.isRunning()) { + try { + // this may block forever, see IDEA-54340 + myState.sendUserInput(textToSend); + } + catch (IOException ignored) { + } + } + } + }, 0); + } + + public Object getData(final String dataId) { + if (PlatformDataKeys.NAVIGATABLE.is(dataId)) { + if (myEditor == null) { + return null; + } + final LogicalPosition pos = myEditor.getCaretModel().getLogicalPosition(); + final HyperlinkInfo info = getHyperlinkInfoByLineAndCol(pos.line, pos.column); + final OpenFileDescriptor openFileDescriptor = info instanceof FileHyperlinkInfo ? ((FileHyperlinkInfo)info).getDescriptor() : null; + if (openFileDescriptor == null || !openFileDescriptor.getFile().isValid()) { + return null; + } + return openFileDescriptor; + } + + if (PlatformDataKeys.EDITOR.is(dataId)) { + return myEditor; + } + if (PlatformDataKeys.HELP_ID.is(dataId)) { + return myHelpId; + } + if (LangDataKeys.CONSOLE_VIEW.is(dataId)) { + return this; + } + return null; + } + + public void setHelpId(final String helpId) { + myHelpId = helpId; + } + + public void addMessageFilter(final Filter filter) { + myCustomFilter.addFilter(filter); + } + + public void printHyperlink(final String hyperlinkText, final HyperlinkInfo info) { + if (myEditor == null) return; + print(hyperlinkText, ConsoleViewContentType.NORMAL_OUTPUT); + flushDeferredText(); + final int textLength = myEditor.getDocument().getTextLength(); + addHyperlink(textLength - hyperlinkText.length(), textLength, null, info, getHyperlinkAttributes()); + } + + public static TextAttributes getHyperlinkAttributes() { + return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); + } + + public static TextAttributes getFollowedHyperlinkAttributes() { + return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.FOLLOWED_HYPERLINK_ATTRIBUTES); + } + + private Editor createEditor() { + return ApplicationManager.getApplication().runReadAction(new Computable() { + public Editor compute() { + return doCreateEditor(); + } + }); + } + + private Editor doCreateEditor() { + final EditorEx editor = createRealEditor(); + editor.addEditorMouseListener(new EditorMouseAdapter() { + public void mouseReleased(final EditorMouseEvent e) { + final MouseEvent mouseEvent = e.getMouseEvent(); + if (!mouseEvent.isPopupTrigger()) { + navigate(e); + } + } + }); + + editor.addEditorMouseListener(new EditorPopupHandler() { + public void invokePopup(final EditorMouseEvent event) { + final MouseEvent mouseEvent = event.getMouseEvent(); + popupInvoked(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY()); + } + }); + + + final int bufferSize = USE_CYCLIC_BUFFER ? CYCLIC_BUFFER_SIZE : 0; + editor.getDocument().setCyclicBufferSize(bufferSize); + + editor.putUserData(CONSOLE_VIEW_IN_EDITOR_VIEW, this); + + editor.getContentComponent().addMouseMotionListener( + new MouseMotionAdapter() { + public void mouseMoved(final MouseEvent e) { + final HyperlinkInfo info = getHyperlinkInfoByPoint(e.getPoint()); + if (info != null) { + editor.getContentComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + else { + editor.getContentComponent().setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + } + } + } + ); + return editor; + } + + protected EditorEx createRealEditor() { + final EditorFactoryImpl document = (EditorFactoryImpl)EditorFactory.getInstance(); + final Document editorDocument = document.createDocument(true); + editorDocument.addDocumentListener(new DocumentListener() { + public void beforeDocumentChange(DocumentEvent event) { + } + + public void documentChanged(DocumentEvent event) { + if (myFileType != null) { + highlightUserTokens(); + } + } + }); + final EditorEx editor = (EditorEx)document.createViewer(editorDocument, myProject); + editor.getSettings().setAllowSingleLogicalLineFolding(true); // We want to fold long soft-wrapped command lines + editor.setSoftWrapAppliancePlace(SoftWrapAppliancePlaces.CONSOLE); + + final EditorHighlighter highlighter = createHighlighter(); + editor.setHighlighter(highlighter); + + final EditorSettings editorSettings = editor.getSettings(); + editorSettings.setLineMarkerAreaShown(false); + editorSettings.setIndentGuidesShown(false); + editorSettings.setLineNumbersShown(false); + editorSettings.setFoldingOutlineShown(true); + editorSettings.setAdditionalPageAtBottom(false); + editorSettings.setAdditionalColumnsCount(0); + editorSettings.setAdditionalLinesCount(0); + + final EditorColorsScheme scheme = editor.getColorsScheme(); + editor.setBackgroundColor(scheme.getColor(ConsoleViewContentType.CONSOLE_BACKGROUND_KEY)); + scheme.setColor(EditorColors.CARET_ROW_COLOR, null); + scheme.setColor(EditorColors.RIGHT_MARGIN_COLOR, null); + + final ConsoleViewImpl consoleView = this; + editor.getContentComponent().addKeyListener(new KeyListener() { + private int historyPosition = myHistory.size(); + + public void keyTyped(KeyEvent e) { + + } + + public void keyPressed(KeyEvent e) { + } + + public void keyReleased(KeyEvent e) { + if (e.isAltDown() && !e.isControlDown() && !e.isMetaDown() && !e.isShiftDown()) { + if (e.getKeyCode() == KeyEvent.VK_UP) { + historyPosition--; + if (historyPosition < 0) historyPosition = 0; + replaceString(); + e.consume(); + } + else if (e.getKeyCode() == KeyEvent.VK_DOWN) { + historyPosition++; + if (historyPosition > myHistory.size()) historyPosition = myHistory.size(); + replaceString(); + e.consume(); + } + } + else { + historyPosition = myHistory.size(); + } + } + + private void replaceString() { + final String str; + + if (myHistory.size() == historyPosition) { + str = ""; + } + else { + str = myHistory.get(historyPosition); + } + synchronized (LOCK) { + if (myTokens.isEmpty()) return; + final TokenInfo info = myTokens.get(myTokens.size() - 1); + if (info.contentType != ConsoleViewContentType.USER_INPUT) { + consoleView.insertUserText(str, 0); + } + else { + consoleView.replaceUserText(str, info.startOffset, info.endOffset); + } + } + } + }); + + setEditorUpActions(editor); + return editor; + } + + protected MyHighlighter createHighlighter() { + return new MyHighlighter(); + } + + private void highlightUserTokens() { + if (myTokens.isEmpty()) return; + final TokenInfo token = myTokens.get(myTokens.size() - 1); + if (token.contentType == ConsoleViewContentType.USER_INPUT) { + String text = myEditor.getDocument().getText().substring(token.startOffset, token.endOffset); + PsiFile file = PsiFileFactory.getInstance(myProject). + createFileFromText("dummy", myFileType, text, LocalTimeCounter.currentTime(), true); + Document document = PsiDocumentManager.getInstance(myProject).getDocument(file); + assert document != null; + Editor editor = EditorFactory.getInstance().createEditor(document, myProject, myFileType, false); + try { + RangeHighlighter[] allHighlighters = myEditor.getMarkupModel().getAllHighlighters(); + for (RangeHighlighter highlighter : allHighlighters) { + if (highlighter.getStartOffset() >= token.startOffset) { + myEditor.getMarkupModel().removeHighlighter(highlighter); + } + } + HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(0); + while (!iterator.atEnd()) { + myEditor.getMarkupModel() + .addRangeHighlighter(iterator.getStart() + token.startOffset, iterator.getEnd() + token.startOffset, HighlighterLayer.SYNTAX, + iterator.getTextAttributes(), + HighlighterTargetArea.EXACT_RANGE); + iterator.advance(); + } + } + finally { + EditorFactory.getInstance().releaseEditor(editor); + } + } + } + + private static void setEditorUpActions(final Editor editor) { + new EnterHandler().registerCustomShortcutSet(CommonShortcuts.ENTER, editor.getContentComponent()); + registerActionHandler(editor, IdeActions.ACTION_EDITOR_PASTE, new PasteHandler()); + registerActionHandler(editor, IdeActions.ACTION_EDITOR_BACKSPACE, new BackSpaceHandler()); + registerActionHandler(editor, IdeActions.ACTION_EDITOR_DELETE, new DeleteHandler()); + } + + private static void registerActionHandler(final Editor editor, final String actionId, final AnAction action) { + final Keymap keymap = KeymapManager.getInstance().getActiveKeymap(); + final Shortcut[] shortcuts = keymap.getShortcuts(actionId); + action.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), editor.getContentComponent()); + } + + private void popupInvoked(final Component component, final int x, final int y) { + final DefaultActionGroup group = new DefaultActionGroup(); + group.add(new ClearAllAction()); + group.add(new CopyAction()); + group.addSeparator(); + final ActionManager actionManager = ActionManager.getInstance(); + final ActionPopupMenu menu = actionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, (ActionGroup)actionManager.getAction(CONSOLE_VIEW_POPUP_MENU)); + menu.getComponent().show(component, x, y); + } + + private void navigate(final EditorMouseEvent event) { + if (event.getMouseEvent().isPopupTrigger()) return; + final Point p = event.getMouseEvent().getPoint(); + final HyperlinkInfo info = getHyperlinkInfoByPoint(p); + if (info != null) { + info.navigate(myProject); + linkFollowed(info); + } + } + + public static final Key OLD_HYPERLINK_TEXT_ATTRIBUTES = Key.create("OLD_HYPERLINK_TEXT_ATTRIBUTES"); + + private void linkFollowed(final HyperlinkInfo info) { + linkFollowed(myEditor, myHyperlinks, info); + } + + public static void linkFollowed(final Editor editor, final Hyperlinks hyperlinks, final HyperlinkInfo info) { + MarkupModelEx markupModel = (MarkupModelEx)editor.getMarkupModel(); + for (Map.Entry entry : hyperlinks.getRanges().entrySet()) { + RangeHighlighter range = entry.getKey(); + TextAttributes oldAttr = range.getUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES); + if (oldAttr != null) { + markupModel.setRangeHighlighterAttributes(range, oldAttr); + range.putUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES, null); + } + if (entry.getValue() == info) { + TextAttributes oldAttributes = range.getTextAttributes(); + range.putUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES, oldAttributes); + TextAttributes attributes = getFollowedHyperlinkAttributes().clone(); + assert oldAttributes != null; + attributes.setFontType(oldAttributes.getFontType()); + attributes.setEffectType(oldAttributes.getEffectType()); + attributes.setEffectColor(oldAttributes.getEffectColor()); + attributes.setForegroundColor(oldAttributes.getForegroundColor()); + markupModel.setRangeHighlighterAttributes(range, attributes); + } + } + //refresh highlighter text attributes + RangeHighlighter dummy = + markupModel.addRangeHighlighter(0, 0, HYPERLINK_LAYER, getHyperlinkAttributes(), HighlighterTargetArea.EXACT_RANGE); + markupModel.removeHighlighter(dummy); + } + + public HyperlinkInfo getHyperlinkInfoByPoint(final Point p) { + return getHyperlinkInfoByPoint(myEditor, myHyperlinks, p); + } + + public static HyperlinkInfo getHyperlinkInfoByPoint(final Editor editor, final Hyperlinks hyperlinks, final Point p) { + final LogicalPosition pos = editor.xyToLogicalPosition(new Point(p.x, p.y)); + return getHyperlinkInfoByLineAndCol(editor, hyperlinks, pos.line, pos.column); + } + + private HyperlinkInfo getHyperlinkInfoByLineAndCol(final int line, final int col) { + return getHyperlinkInfoByLineAndCol(myEditor, myHyperlinks, line, col); + } + + public static HyperlinkInfo getHyperlinkInfoByLineAndCol(final Editor editor, + final Hyperlinks hyperlinks, + final int line, + final int col) { + final int offset = editor.logicalPositionToOffset(new LogicalPosition(line, col)); + return hyperlinks.getHyperlinkAt(offset); + } + + private void highlightHyperlinksAndFoldings(final int line1, final int endLine) { + ApplicationManager.getApplication().assertIsDispatchThread(); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + highlightHyperlinks(myEditor, myHyperlinks, myCustomFilter, myPredefinedMessageFilter, line1, endLine); + updateFoldings(line1, endLine, false); + } + + private void updateFoldings(final int line1, final int endLine, boolean immediately) { + final Document document = myEditor.getDocument(); + final CharSequence chars = document.getCharsSequence(); + final int startLine = Math.max(0, line1); + final List toAdd = new ArrayList(); + for (int line = startLine; line <= endLine; line++) { + addFolding(document, chars, line, toAdd); + } + if (!toAdd.isEmpty()) { + doUpdateFolding(toAdd, immediately); + } + } + + public static void highlightHyperlinks(final Editor editor, + final Hyperlinks hyperlinks, + final Filter myCustomFilter, + final Filter myPredefinedMessageFilter, + final int line1, final int endLine) { + final Document document = editor.getDocument(); + final TextAttributes hyperlinkAttributes = getHyperlinkAttributes(); + + final int startLine = Math.max(0, line1); + + for (int line = startLine; line <= endLine; line++) { + int endOffset = document.getLineEndOffset(line); + if (endOffset < document.getTextLength()) { + endOffset++; // add '\n' + } + final String text = getLineText(document, line, true); + Filter.Result result = myCustomFilter.applyFilter(text, endOffset); + if (result == null) { + result = myPredefinedMessageFilter.applyFilter(text, endOffset); + } + if (result != null) { + final int highlightStartOffset = result.highlightStartOffset; + final int highlightEndOffset = result.highlightEndOffset; + final HyperlinkInfo hyperlinkInfo = result.hyperlinkInfo; + addHyperlink(editor, hyperlinks, highlightStartOffset, highlightEndOffset, result.highlightAttributes, hyperlinkInfo, + hyperlinkAttributes); + } + } + } + + private void doUpdateFolding(final List toAdd, final boolean immediately) { + assertIsDispatchThread(); + myPendingFoldRegions.addAll(toAdd); + + myFoldingAlarm.cancelAllRequests(); + final Runnable runnable = new Runnable() { + public void run() { + assertIsDispatchThread(); + final FoldingModel model = myEditor.getFoldingModel(); + final Runnable operation = new Runnable() { + public void run() { + assertIsDispatchThread(); + for (FoldRegion region : myPendingFoldRegions) { + region.setExpanded(false); + model.addFoldRegion(region); + } + myPendingFoldRegions.clear(); + } + }; + if (immediately) { + model.runBatchFoldingOperation(operation); + } + else { + model.runBatchFoldingOperationDoNotCollapseCaret(operation); + } + } + }; + if (immediately || myPendingFoldRegions.size() > 100) { + runnable.run(); + } + else { + myFoldingAlarm.addRequest(runnable, 50); + } + } + + private void addFolding(Document document, CharSequence chars, int line, List toAdd) { + String commandLinePlaceholder = myCommandLineFolding.getPlaceholder(line); + if (commandLinePlaceholder != null) { + FoldRegion region = ((FoldingModelEx)myEditor.getFoldingModel()).createFoldRegion( + document.getLineStartOffset(line), document.getLineEndOffset(line), commandLinePlaceholder, null + ); + toAdd.add(region); + return; + } + ConsoleFolding current = foldingForLine(getLineText(document, line, false)); + if (current != null) { + myFolding.put(line, current); + } + + final ConsoleFolding prevFolding = myFolding.get(line - 1); + if (current == null && prevFolding != null) { + final int lEnd = line - 1; + int lStart = lEnd; + while (prevFolding.equals(myFolding.get(lStart - 1))) lStart--; + if (lStart == lEnd) { + return; + } + + for (int i = lStart; i <= lEnd; i++) { + myFolding.remove(i); + } + + List toFold = new ArrayList(lEnd - lStart + 1); + for (int i = lStart; i <= lEnd; i++) { + toFold.add(getLineText(document, i, false)); + } + + int oStart = document.getLineStartOffset(lStart); + if (oStart > 0) oStart--; + int oEnd = CharArrayUtil.shiftBackward(chars, document.getLineEndOffset(lEnd) - 1, " \t") + 1; + + FoldRegion region = + ((FoldingModelEx)myEditor.getFoldingModel()).createFoldRegion(oStart, oEnd, prevFolding.getPlaceholderText(toFold), null); + if (region != null) { + toAdd.add(region); + } + } + } + + public static String getLineText(Document document, int lineNumber, boolean includeEol) { + int endOffset = document.getLineEndOffset(lineNumber); + if (includeEol && endOffset < document.getTextLength()) { + endOffset++; + } + return document.getCharsSequence().subSequence(document.getLineStartOffset(lineNumber), endOffset).toString(); + } + + @Nullable + private static ConsoleFolding foldingForLine(String lineText) { + for (ConsoleFolding folding : ConsoleFolding.EP_NAME.getExtensions()) { + if (folding.shouldFoldLine(lineText)) { + return folding; + } + } + return null; + } + + private void addHyperlink(final int highlightStartOffset, + final int highlightEndOffset, + final TextAttributes highlightAttributes, + final HyperlinkInfo hyperlinkInfo, + final TextAttributes hyperlinkAttributes) { + addHyperlink(myEditor, myHyperlinks, highlightStartOffset, highlightEndOffset, highlightAttributes, hyperlinkInfo, hyperlinkAttributes); + } + + private static void addHyperlink(final Editor editor, + final Hyperlinks hyperlinks, + final int highlightStartOffset, + final int highlightEndOffset, + final TextAttributes highlightAttributes, + final HyperlinkInfo hyperlinkInfo, + final TextAttributes hyperlinkAttributes) { + TextAttributes textAttributes = highlightAttributes != null ? highlightAttributes : hyperlinkAttributes; + final RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(highlightStartOffset, + highlightEndOffset, + HYPERLINK_LAYER, + textAttributes, + HighlighterTargetArea.EXACT_RANGE); + hyperlinks.add(highlighter, hyperlinkInfo); + } + + public static class ClearAllAction extends DumbAwareAction { + public ClearAllAction() { + super(ExecutionBundle.message("clear.all.from.console.action.name")); + } + + @Override + public void update(AnActionEvent e) { + final boolean enabled = e.getData(LangDataKeys.CONSOLE_VIEW) != null; + e.getPresentation().setEnabled(enabled); + e.getPresentation().setVisible(enabled); + } + + public void actionPerformed(final AnActionEvent e) { + final ConsoleView consoleView = e.getData(LangDataKeys.CONSOLE_VIEW); + if (consoleView != null) { + consoleView.clear(); + } + } + } + + public static class CopyAction extends DumbAwareAction { + + @Override + public void update(AnActionEvent e) { + final Editor editor = e.getData(PlatformDataKeys.EDITOR); + final boolean enabled = editor != null && e.getData(LangDataKeys.CONSOLE_VIEW) != null; + e.getPresentation().setEnabled(enabled); + e.getPresentation().setVisible(enabled); + + e.getPresentation().setText(editor != null && editor.getSelectionModel().hasSelection() + ? ExecutionBundle.message("copy.selected.content.action.name") + : ExecutionBundle.message("copy.content.action.name")); + } + + public void actionPerformed(final AnActionEvent e) { + final Editor editor = e.getData(PlatformDataKeys.EDITOR); + assert editor != null; + if (editor.getSelectionModel().hasSelection()) { + editor.getSelectionModel().copySelectionToClipboard(); + } + else { + editor.getSelectionModel().setSelection(0, editor.getDocument().getTextLength()); + editor.getSelectionModel().copySelectionToClipboard(); + editor.getSelectionModel().removeSelection(); + } + } + } + + private class MyHighlighter extends DocumentAdapter implements EditorHighlighter { + private HighlighterClient myEditor; + + public HighlighterIterator createIterator(final int startOffset) { + final int startIndex = findTokenInfoIndexByOffset(startOffset); + + return new HighlighterIterator() { + private int myIndex = startIndex; + + public TextAttributes getTextAttributes() { + if (myFileType != null && getTokenInfo().contentType == ConsoleViewContentType.USER_INPUT) { + return ConsoleViewContentType.NORMAL_OUTPUT.getAttributes(); + } + return getTokenInfo() == null ? null : getTokenInfo().attributes; + } + + public int getStart() { + return getTokenInfo() == null ? 0 : getTokenInfo().startOffset; + } + + public int getEnd() { + return getTokenInfo() == null ? 0 : getTokenInfo().endOffset; + } + + public IElementType getTokenType() { + return null; + } + + public void advance() { + myIndex++; + } + + public void retreat() { + myIndex--; + } + + public boolean atEnd() { + return myIndex < 0 || myIndex >= myTokens.size(); + } + + public Document getDocument() { + return myEditor.getDocument(); + } + + private TokenInfo getTokenInfo() { + return myTokens.get(myIndex); + } + }; + } + + public void setText(final CharSequence text) { + } + + public void setEditor(final HighlighterClient editor) { + LOG.assertTrue(myEditor == null, "Highlighters cannot be reused with different editors"); + myEditor = editor; + } + + public void setColorScheme(EditorColorsScheme scheme) { + } + } + + private int findTokenInfoIndexByOffset(final int offset) { + int low = 0; + int high = myTokens.size() - 1; + + while (low <= high) { + final int mid = (low + high) / 2; + final TokenInfo midVal = myTokens.get(mid); + if (offset < midVal.startOffset) { + high = mid - 1; + } + else if (offset >= midVal.endOffset) { + low = mid + 1; + } + else { + return mid; + } + } + return myTokens.size(); + } + + private static class MyTypedHandler implements TypedActionHandler { + private final TypedActionHandler myOriginalHandler; + + private MyTypedHandler(final TypedActionHandler originalAction) { + myOriginalHandler = originalAction; + } + + public void execute(@NotNull final Editor editor, final char charTyped, @NotNull final DataContext dataContext) { + final ConsoleViewImpl consoleView = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW); + if (consoleView == null || !consoleView.myState.isRunning() || consoleView.isViewer) { + myOriginalHandler.execute(editor, charTyped, dataContext); + } + else { + final String s = String.valueOf(charTyped); + SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + } + else { + consoleView.insertUserText(s, editor.getCaretModel().getOffset()); + } + } + } + } + + private abstract static class ConsoleAction extends AnAction implements DumbAware { + public void actionPerformed(final AnActionEvent e) { + final DataContext context = e.getDataContext(); + final ConsoleViewImpl console = getRunningConsole(context); + execute(console, context); + } + + protected abstract void execute(ConsoleViewImpl console, final DataContext context); + + public void update(final AnActionEvent e) { + final ConsoleViewImpl console = getRunningConsole(e.getDataContext()); + e.getPresentation().setEnabled(console != null); + } + + @Nullable + private static ConsoleViewImpl getRunningConsole(final DataContext context) { + final Editor editor = PlatformDataKeys.EDITOR.getData(context); + if (editor != null) { + final ConsoleViewImpl console = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW); + if (console != null && console.myState.isRunning()) { + return console; + } + } + return null; + } + } + + private static class EnterHandler extends ConsoleAction { + public void execute(final ConsoleViewImpl consoleView, final DataContext context) { + synchronized (consoleView.LOCK) { + String str = consoleView.myDeferredUserInput.toString(); + if (StringUtil.isNotEmpty(str)) { + consoleView.myHistory.remove(str); + consoleView.myHistory.add(str); + if (consoleView.myHistory.size() > consoleView.myHistorySize) consoleView.myHistory.remove(0); + } + for (ConsoleInputListener listener : consoleView.myConsoleInputListeners) { + listener.textEntered(str); + } + } + consoleView.print("\n", ConsoleViewContentType.USER_INPUT); + consoleView.flushDeferredText(); + final Editor editor = consoleView.myEditor; + editor.getCaretModel().moveToOffset(editor.getDocument().getTextLength()); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + } + + private static class PasteHandler extends ConsoleAction { + public void execute(final ConsoleViewImpl consoleView, final DataContext context) { + final Transferable content = CopyPasteManager.getInstance().getContents(); + if (content == null) return; + String s = null; + try { + s = (String)content.getTransferData(DataFlavor.stringFlavor); + } + catch (Exception e) { + consoleView.getToolkit().beep(); + } + if (s == null) return; + Editor editor = consoleView.myEditor; + SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + consoleView.replaceUserText(s, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + } + else { + consoleView.insertUserText(s, editor.getCaretModel().getOffset()); + } + } + } + + private static class BackSpaceHandler extends ConsoleAction { + public void execute(final ConsoleViewImpl consoleView, final DataContext context) { + final Editor editor = consoleView.myEditor; + + if (IncrementalSearchHandler.isHintVisible(editor)) { + getDefaultActionHandler().execute(editor, context); + return; + } + + final Document document = editor.getDocument(); + final int length = document.getTextLength(); + if (length == 0) { + return; + } + + SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + consoleView.deleteUserText(selectionModel.getSelectionStart(), + selectionModel.getSelectionEnd() - selectionModel.getSelectionStart()); + } + else if (editor.getCaretModel().getOffset() > 0) { + consoleView.deleteUserText(editor.getCaretModel().getOffset() - 1, 1); + } + } + + private static EditorActionHandler getDefaultActionHandler() { + return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); + } + } + + private static class DeleteHandler extends ConsoleAction { + public void execute(final ConsoleViewImpl consoleView, final DataContext context) { + final Editor editor = consoleView.myEditor; + + if (IncrementalSearchHandler.isHintVisible(editor)) { + getDefaultActionHandler().execute(editor, context); + return; + } + + final Document document = editor.getDocument(); + final int length = document.getTextLength(); + if (length == 0) { + return; + } + + SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + consoleView.deleteUserText(selectionModel.getSelectionStart(), + selectionModel.getSelectionEnd() - selectionModel.getSelectionStart()); + } + else { + consoleView.deleteUserText(editor.getCaretModel().getOffset(), 1); + } + } + + private static EditorActionHandler getDefaultActionHandler() { + return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE); + } + } + + public static class Hyperlinks { + private static final int NO_INDEX = Integer.MIN_VALUE; + private final Map myHighlighterToMessageInfoMap = new HashMap(); + private int myLastIndex = NO_INDEX; + + public void clear() { + myHighlighterToMessageInfoMap.clear(); + myLastIndex = NO_INDEX; + } + + public HyperlinkInfo getHyperlinkAt(final int offset) { + for (final RangeHighlighter highlighter : myHighlighterToMessageInfoMap.keySet()) { + if (highlighter.isValid() && containsOffset(offset, highlighter)) { + return myHighlighterToMessageInfoMap.get(highlighter); + } + } + return null; + } + + private static boolean containsOffset(final int offset, final RangeHighlighter highlighter) { + return highlighter.getStartOffset() <= offset && offset <= highlighter.getEndOffset(); + } + + public void add(final RangeHighlighter highlighter, final HyperlinkInfo hyperlinkInfo) { + myHighlighterToMessageInfoMap.put(highlighter, hyperlinkInfo); + if (myLastIndex != NO_INDEX && containsOffset(myLastIndex, highlighter)) myLastIndex = NO_INDEX; + } + + public Map getRanges() { + return myHighlighterToMessageInfoMap; + } + } + + public JComponent getPreferredFocusableComponent() { + //ensure editor created + getComponent(); + return myEditor.getContentComponent(); + } + + + // navigate up/down in stack trace + public boolean hasNextOccurence() { + return next(1, false) != null; + } + + public boolean hasPreviousOccurence() { + return next(-1, false) != null; + } + + public OccurenceInfo goNextOccurence() { + return next(1, true); + } + + @Nullable + private OccurenceInfo next(final int delta, boolean doMove) { + List ranges = new ArrayList(myHyperlinks.getRanges().keySet()); + for (Iterator iterator = ranges.iterator(); iterator.hasNext();) { + RangeHighlighter highlighter = iterator.next(); + if (myEditor.getFoldingModel().getCollapsedRegionAtOffset(highlighter.getStartOffset()) != null) { + iterator.remove(); + } + } + Collections.sort(ranges, new Comparator() { + public int compare(final RangeHighlighter o1, final RangeHighlighter o2) { + return o1.getStartOffset() - o2.getStartOffset(); + } + }); + int i; + for (i = 0; i < ranges.size(); i++) { + RangeHighlighter range = ranges.get(i); + if (range.getUserData(OLD_HYPERLINK_TEXT_ATTRIBUTES) != null) { + break; + } + } + int newIndex = ranges.isEmpty() ? -1 : i == ranges.size() ? 0 : (i + delta + ranges.size()) % ranges.size(); + RangeHighlighter next = newIndex < ranges.size() && newIndex >= 0 ? ranges.get(newIndex) : null; + if (next == null) return null; + if (doMove) { + scrollTo(next.getStartOffset()); + } + final HyperlinkInfo hyperlinkInfo = myHyperlinks.getRanges().get(next); + return hyperlinkInfo == null ? null : new OccurenceInfo(new Navigatable.Adapter() { + public void navigate(final boolean requestFocus) { + hyperlinkInfo.navigate(myProject); + linkFollowed(hyperlinkInfo); + } + }, i, ranges.size()); + } + + public OccurenceInfo goPreviousOccurence() { + return next(-1, true); + } + + public String getNextOccurenceActionName() { + return ExecutionBundle.message("down.the.stack.trace"); + } + + public String getPreviousOccurenceActionName() { + return ExecutionBundle.message("up.the.stack.trace"); + } + + public void addCustomConsoleAction(@NotNull AnAction action) { + customActions.add(action); + } + + @NotNull + public AnAction[] createConsoleActions() { + //Initializing prev and next occurrences actions + final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); + final AnAction prevAction = actionsManager.createPrevOccurenceAction(this); + prevAction.getTemplatePresentation().setText(getPreviousOccurenceActionName()); + final AnAction nextAction = actionsManager.createNextOccurenceAction(this); + nextAction.getTemplatePresentation().setText(getNextOccurenceActionName()); + + final AnAction switchSoftWrapsAction = new ToggleUseSoftWrapsToolbarAction(SoftWrapAppliancePlaces.CONSOLE) { + @Override + protected Editor getEditor(AnActionEvent e) { + return myEditor; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + super.setSelected(e, state); + EditorSettingsExternalizable.getInstance().setUseSoftWraps(myEditor.getSettings().isUseSoftWraps(), SoftWrapAppliancePlaces.CONSOLE); + } + }; + final AnAction autoScrollToTheEndAction = new ScrollToTheEndToolbarAction() { + @Override + public void actionPerformed(final AnActionEvent e) { + scrollToTheEnd(); + } + }; + + //Initializing custom actions + final AnAction[] consoleActions = new AnAction[4 + customActions.size()]; + consoleActions[0] = prevAction; + consoleActions[1] = nextAction; + consoleActions[2] = switchSoftWrapsAction; + consoleActions[3] = autoScrollToTheEndAction; + for (int i = 0; i < customActions.size(); ++i) { + consoleActions[i + 4] = customActions.get(i); + } + return consoleActions; + } + + protected void scrollToTheEnd() { + myEditor.getCaretModel().moveToOffset(myEditor.getDocument().getTextLength()); + myEditor.getSelectionModel().removeSelection(); + myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + + public void setEditorEnabled(boolean enabled) { + myEditor.getContentComponent().setEnabled(enabled); + } + + private void fireChange() { + if (myDeferredTypes.isEmpty()) return; + Collection types = Collections.unmodifiableCollection(myDeferredTypes); + + for (ChangeListener each : myListeners) { + each.contentAdded(types); + } + + myDeferredTypes.clear(); + } + + public void addChangeListener(final ChangeListener listener, final Disposable parent) { + myListeners.add(listener); + Disposer.register(parent, new Disposable() { + public void dispose() { + myListeners.remove(listener); + } + }); + } + + /** + * insert text to document + * + * @param s inserted text + * @param offset relatively to all document text + */ + private void insertUserText(final String s, int offset) { + final ConsoleViewImpl consoleView = this; + final Editor editor = consoleView.myEditor; + final Document document = editor.getDocument(); + final int startOffset; + + synchronized (consoleView.LOCK) { + if (consoleView.myTokens.isEmpty()) return; + final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + if (info.contentType != ConsoleViewContentType.USER_INPUT && !s.contains("\n")) { + consoleView.print(s, ConsoleViewContentType.USER_INPUT); + consoleView.flushDeferredText(); + editor.getCaretModel().moveToOffset(document.getTextLength()); + editor.getSelectionModel().removeSelection(); + return; + } + else if (info.contentType != ConsoleViewContentType.USER_INPUT) { + insertUserText("temp", offset); + final TokenInfo newInfo = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + replaceUserText(s, newInfo.startOffset, newInfo.endOffset); + return; + } + + final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); + if (offset > info.endOffset) { + startOffset = info.endOffset; + } + else { + startOffset = Math.max(deferredOffset, Math.max(info.startOffset, offset)); + } + + consoleView.myDeferredUserInput.insert(startOffset - deferredOffset, s); + + int charCountToAdd = s.length(); + info.endOffset += charCountToAdd; + consoleView.myContentSize += charCountToAdd; + } + + document.insertString(startOffset, s); + // Math.max is needed when cyclic buffer is used + editor.getCaretModel().moveToOffset(Math.min(startOffset + s.length(), document.getTextLength())); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + + /** + * replace text + * + * @param s text for replace + * @param start relativly to all document text + * @param end relativly to all document text + */ + private void replaceUserText(final String s, int start, int end) { + if (start == end) { + insertUserText(s, start); + return; + } + final ConsoleViewImpl consoleView = this; + final Editor editor = consoleView.myEditor; + final Document document = editor.getDocument(); + final int startOffset; + final int endOffset; + + synchronized (consoleView.LOCK) { + if (consoleView.myTokens.isEmpty()) return; + final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + if (info.contentType != ConsoleViewContentType.USER_INPUT) { + consoleView.print(s, ConsoleViewContentType.USER_INPUT); + consoleView.flushDeferredText(); + editor.getCaretModel().moveToOffset(document.getTextLength()); + editor.getSelectionModel().removeSelection(); + return; + } + if (consoleView.myDeferredUserInput.length() == 0) return; + + final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); + + startOffset = getStartOffset(start, info, deferredOffset); + endOffset = getEndOffset(end, info); + + if (startOffset == -1 || + endOffset == -1 || + endOffset <= startOffset) { + editor.getSelectionModel().removeSelection(); + editor.getCaretModel().moveToOffset(start); + return; + } + int charCountToReplace = s.length() - endOffset + startOffset; + + consoleView.myDeferredUserInput.replace(startOffset - deferredOffset, endOffset - deferredOffset, s); + + info.endOffset += charCountToReplace; + if (info.startOffset == info.endOffset) { + consoleView.myTokens.remove(consoleView.myTokens.size() - 1); + } + consoleView.myContentSize += charCountToReplace; + } + + document.replaceString(startOffset, endOffset, s); + editor.getCaretModel().moveToOffset(startOffset + s.length()); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + editor.getSelectionModel().removeSelection(); + } + + /** + * delete text + * + * @param offset relativly to all document text + * @param length lenght of deleted text + */ + private void deleteUserText(int offset, int length) { + ConsoleViewImpl consoleView = this; + final Editor editor = consoleView.myEditor; + final Document document = editor.getDocument(); + final int startOffset; + final int endOffset; + + synchronized (consoleView.LOCK) { + if (consoleView.myTokens.isEmpty()) return; + final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1); + if (info.contentType != ConsoleViewContentType.USER_INPUT) return; + if (consoleView.myDeferredUserInput.length() == 0) return; + + final int deferredOffset = myContentSize - consoleView.myDeferredUserInput.length(); + startOffset = getStartOffset(offset, info, deferredOffset); + endOffset = getEndOffset(offset + length, info); + if (startOffset == -1 || + endOffset == -1 || + endOffset <= startOffset || + startOffset < deferredOffset) { + editor.getSelectionModel().removeSelection(); + editor.getCaretModel().moveToOffset(offset); + return; + } + + consoleView.myDeferredUserInput.delete(startOffset - deferredOffset, endOffset - deferredOffset); + int charCountToDelete = endOffset - startOffset; + + info.endOffset -= charCountToDelete; + if (info.startOffset == info.endOffset) { + consoleView.myTokens.remove(consoleView.myTokens.size() - 1); + } + consoleView.myContentSize -= charCountToDelete; + } + + document.deleteString(startOffset, endOffset); + editor.getCaretModel().moveToOffset(startOffset); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + editor.getSelectionModel().removeSelection(); + } + + //util methods for add, replace, delete methods + private static int getStartOffset(int offset, TokenInfo info, int deferredOffset) { + int startOffset; + if (offset >= info.startOffset && offset < info.endOffset) { + startOffset = Math.max(offset, deferredOffset); + } + else if (offset < info.startOffset) { + startOffset = Math.max(info.startOffset, deferredOffset); + } + else { + startOffset = -1; + } + return startOffset; + } + + private static int getEndOffset(int offset, TokenInfo info) { + int endOffset; + if (offset > info.endOffset) { + endOffset = info.endOffset; + } + else if (offset <= info.startOffset) { + endOffset = -1; + } + else { + endOffset = offset; + } + return endOffset; + } + + /** + * Command line used to launch application/test from idea is quite a big most of the time (it's likely that classpath definition + * takes a lot of space). Hence, it takes many visual lines during representation if soft wraps are enabled. + *

+ * Our point is to fold such long command line and represent it as a single visual line by default. + */ + private class CommandLineFolding extends ConsoleFolding { + + /** + * Checks if target line should be folded and returns its placeholder if the examination succeeds. + * + * @param line index of line to check + * @return placeholder text if given line should be folded; null otherwise + */ + @Nullable + public String getPlaceholder(int line) { + if (myEditor == null || line != 0 || !myEditor.getSettings().isUseSoftWraps()) { + return null; + } + + String text = getLineText(myEditor.getDocument(), line, false); + if (text.length() < 1000) { + return null; + } + // Don't fold the first line if no soft wraps are used or if the line is not that big. + if (!myEditor.getSettings().isUseSoftWraps() || text.length() < 1000) { + return null; + } + boolean nonWhiteSpaceFound = false; + int index = 0; + for (; index < text.length(); index++) { + char c = text.charAt(index); + if (c != ' ' && c != '\t') { + nonWhiteSpaceFound = true; + continue; + } + if (nonWhiteSpaceFound) { + break; + } + } + if (index > text.length()) { + // Don't expect to be here + return "<...>"; + } + return text.substring(0, index) + " ..."; + } + + @Override + public boolean shouldFoldLine(String line) { + return false; + } + + @Override + public String getPlaceholderText(List lines) { + // Is not expected to be called. + return "<...>"; + } + } +} + diff --git a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java index b5ade9092935..31e2b6dd54ac 100644 --- a/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/OSProcessHandler.java @@ -1,309 +1,309 @@ -/* - * Copyright 2000-2009 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.process; - -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.vfs.encoding.EncodingManager; -import com.intellij.util.Consumer; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.concurrent.*; - -public class OSProcessHandler extends ProcessHandler { - private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.OSProcessHandler"); - private final Process myProcess; - private final String myCommandLine; - - private final ProcessWaitFor myWaitFor; - - private static class ExecutorServiceHolder { - private static final ExecutorService ourThreadExecutorsService = createServiceImpl(); - - private static ThreadPoolExecutor createServiceImpl() { - return new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() { - @SuppressWarnings({"HardCodedStringLiteral"}) - public Thread newThread(Runnable r) { - return new Thread(r, "OSProcessHandler pooled thread"); - } - }); - } - } - - /** - * Override this method in order to execute the task with a custom pool - * - * @param task a task to run - */ - protected Future executeOnPooledThread(Runnable task) { - final Application application = ApplicationManager.getApplication(); - - if (application != null) { - return application.executeOnPooledThread(task); - } - - return ExecutorServiceHolder.ourThreadExecutorsService.submit(task); - } - - public OSProcessHandler(final Process process, final String commandLine) { - myProcess = process; - myCommandLine = commandLine; - myWaitFor = new ProcessWaitFor(process); - } - - private class ProcessWaitFor { - private final Future myWaitForThreadFuture; - private final BlockingQueue> myTerminationCallback = new ArrayBlockingQueue>(1); - - public void detach() { - myWaitForThreadFuture.cancel(true); - } - - - public ProcessWaitFor(final Process process) { - myWaitForThreadFuture = executeOnPooledThread(new Runnable() { - public void run() { - int exitCode = 0; - try { - exitCode = process.waitFor(); - } - catch (InterruptedException ignored) { - } - finally { - try { - myTerminationCallback.take().consume(exitCode); - } - catch (InterruptedException e) { - // Ignore - } - } - } - }); - } - - public void setTerminationCallback(Consumer r) { - myTerminationCallback.offer(r); - } - } - - public Process getProcess() { - return myProcess; - } - - public void startNotify() { - final ReadProcessThread stdoutThread = new ReadProcessThread(createProcessOutReader()) { - protected void textAvailable(String s) { - notifyTextAvailable(s, ProcessOutputTypes.STDOUT); - } - }; - - final ReadProcessThread stderrThread = new ReadProcessThread(createProcessErrReader()) { - protected void textAvailable(String s) { - notifyTextAvailable(s, ProcessOutputTypes.STDERR); - } - }; - - notifyTextAvailable(myCommandLine + '\n', ProcessOutputTypes.SYSTEM); - - addProcessListener(new ProcessAdapter() { - public void startNotified(final ProcessEvent event) { - try { - final Future stdOutReadingFuture = executeOnPooledThread(stdoutThread); - final Future stdErrReadingFuture = executeOnPooledThread(stderrThread); - - myWaitFor.setTerminationCallback(new Consumer() { - @Override - public void consume(Integer exitCode) { - try { - // tell threads that no more attempts to read process' output should be made - stderrThread.setProcessTerminated(true); - stdoutThread.setProcessTerminated(true); - - stdErrReadingFuture.get(); - stdOutReadingFuture.get(); - } - catch (InterruptedException ignored) { - } - catch (ExecutionException e) { - LOG.error(e); - } - finally { - onOSProcessTerminated(exitCode); - } - } - }); - } - finally { - removeProcessListener(this); - } - } - }); - - super.startNotify(); - } - - protected void onOSProcessTerminated(final int exitCode) { - notifyProcessTerminated(exitCode); - } - - protected Reader createProcessOutReader() { - return new InputStreamReader(myProcess.getInputStream(), getCharset()); - } - - protected Reader createProcessErrReader() { - return new InputStreamReader(myProcess.getErrorStream(), getCharset()); - } - - protected void destroyProcessImpl() { - try { - closeStreams(); - } - finally { - myProcess.destroy(); - } - } - - protected void detachProcessImpl() { - final Runnable runnable = new Runnable() { - public void run() { - closeStreams(); - - myWaitFor.detach(); - notifyProcessDetached(); - } - }; - - executeOnPooledThread(runnable); - } - - private void closeStreams() { - try { - myProcess.getOutputStream().close(); - } - catch (IOException e) { - LOG.error(e); - } - } - - public boolean detachIsDefault() { - return false; - } - - public OutputStream getProcessInput() { - return myProcess.getOutputStream(); - } - - // todo: to remove - public String getCommandLine() { - return myCommandLine; - } - - - public Charset getCharset() { - return EncodingManager.getInstance().getDefaultCharset(); - } - - private abstract static class ReadProcessThread implements Runnable { - private final Reader myReader; - private boolean skipLF = false; - - private boolean myIsProcessTerminated = false; - private final char[] myBuffer = new char[8192]; - - public ReadProcessThread(final Reader reader) { - myReader = reader; - } - - public synchronized void setProcessTerminated(boolean isProcessTerminated) { - myIsProcessTerminated = isProcessTerminated; - } - - public void run() { - try { - while (true) { - final int rc = readAvailable(); - if (rc == DONE) break; - Thread.sleep(rc == READ_SOME ? 1L : 50L); - } - } - catch (InterruptedException ignore) { - } - catch (Exception e) { - LOG.error(e); - } - } - - private static final int DONE = 0; - private static final int READ_SOME = 1; - private static final int READ_NONE = 2; - - private synchronized int readAvailable() throws IOException { - char[] buffer = myBuffer; - StringBuilder token = new StringBuilder(); - int rc = READ_NONE; - while (myReader.ready()) { - int n = myReader.read(buffer); - if (n <= 0) break; - rc = READ_SOME; - - for (int i = 0; i < n; i++) { - char c = buffer[i]; - if (skipLF && c != '\n') { - token.append('\r'); - } - - if (c == '\r') { - skipLF = true; - } - else { - skipLF = false; - token.append(c); - } - - if (c == '\n') { - textAvailable(token.toString()); - token.setLength(0); - } - } - } - - if (token.length() != 0) { - textAvailable(token.toString()); - token.setLength(0); - } - - if (myIsProcessTerminated) { - try { - myReader.close(); - } - catch (IOException e1) { - // supressed - } - - return DONE; - } - - return rc; - } - - protected abstract void textAvailable(final String s); - } -} +/* + * Copyright 2000-2009 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.process; + +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.encoding.EncodingManager; +import com.intellij.util.Consumer; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.concurrent.*; + +public class OSProcessHandler extends ProcessHandler { + private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.OSProcessHandler"); + private final Process myProcess; + private final String myCommandLine; + + private final ProcessWaitFor myWaitFor; + + private static class ExecutorServiceHolder { + private static final ExecutorService ourThreadExecutorsService = createServiceImpl(); + + private static ThreadPoolExecutor createServiceImpl() { + return new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() { + @SuppressWarnings({"HardCodedStringLiteral"}) + public Thread newThread(Runnable r) { + return new Thread(r, "OSProcessHandler pooled thread"); + } + }); + } + } + + /** + * Override this method in order to execute the task with a custom pool + * + * @param task a task to run + */ + protected Future executeOnPooledThread(Runnable task) { + final Application application = ApplicationManager.getApplication(); + + if (application != null) { + return application.executeOnPooledThread(task); + } + + return ExecutorServiceHolder.ourThreadExecutorsService.submit(task); + } + + public OSProcessHandler(final Process process, final String commandLine) { + myProcess = process; + myCommandLine = commandLine; + myWaitFor = new ProcessWaitFor(process); + } + + private class ProcessWaitFor { + private final Future myWaitForThreadFuture; + private final BlockingQueue> myTerminationCallback = new ArrayBlockingQueue>(1); + + public void detach() { + myWaitForThreadFuture.cancel(true); + } + + + public ProcessWaitFor(final Process process) { + myWaitForThreadFuture = executeOnPooledThread(new Runnable() { + public void run() { + int exitCode = 0; + try { + exitCode = process.waitFor(); + } + catch (InterruptedException ignored) { + } + finally { + try { + myTerminationCallback.take().consume(exitCode); + } + catch (InterruptedException e) { + // Ignore + } + } + } + }); + } + + public void setTerminationCallback(Consumer r) { + myTerminationCallback.offer(r); + } + } + + public Process getProcess() { + return myProcess; + } + + public void startNotify() { + final ReadProcessThread stdoutThread = new ReadProcessThread(createProcessOutReader()) { + protected void textAvailable(String s) { + notifyTextAvailable(s, ProcessOutputTypes.STDOUT); + } + }; + + final ReadProcessThread stderrThread = new ReadProcessThread(createProcessErrReader()) { + protected void textAvailable(String s) { + notifyTextAvailable(s, ProcessOutputTypes.STDERR); + } + }; + + notifyTextAvailable(myCommandLine + '\n', ProcessOutputTypes.SYSTEM); + + addProcessListener(new ProcessAdapter() { + public void startNotified(final ProcessEvent event) { + try { + final Future stdOutReadingFuture = executeOnPooledThread(stdoutThread); + final Future stdErrReadingFuture = executeOnPooledThread(stderrThread); + + myWaitFor.setTerminationCallback(new Consumer() { + @Override + public void consume(Integer exitCode) { + try { + // tell threads that no more attempts to read process' output should be made + stderrThread.setProcessTerminated(true); + stdoutThread.setProcessTerminated(true); + + stdErrReadingFuture.get(); + stdOutReadingFuture.get(); + } + catch (InterruptedException ignored) { + } + catch (ExecutionException e) { + LOG.error(e); + } + finally { + onOSProcessTerminated(exitCode); + } + } + }); + } + finally { + removeProcessListener(this); + } + } + }); + + super.startNotify(); + } + + protected void onOSProcessTerminated(final int exitCode) { + notifyProcessTerminated(exitCode); + } + + protected Reader createProcessOutReader() { + return new InputStreamReader(myProcess.getInputStream(), getCharset()); + } + + protected Reader createProcessErrReader() { + return new InputStreamReader(myProcess.getErrorStream(), getCharset()); + } + + protected void destroyProcessImpl() { + try { + closeStreams(); + } + finally { + myProcess.destroy(); + } + } + + protected void detachProcessImpl() { + final Runnable runnable = new Runnable() { + public void run() { + closeStreams(); + + myWaitFor.detach(); + notifyProcessDetached(); + } + }; + + executeOnPooledThread(runnable); + } + + private void closeStreams() { + try { + myProcess.getOutputStream().close(); + } + catch (IOException e) { + LOG.error(e); + } + } + + public boolean detachIsDefault() { + return false; + } + + public OutputStream getProcessInput() { + return myProcess.getOutputStream(); + } + + // todo: to remove + public String getCommandLine() { + return myCommandLine; + } + + + public Charset getCharset() { + return EncodingManager.getInstance().getDefaultCharset(); + } + + private abstract static class ReadProcessThread implements Runnable { + private final Reader myReader; + private boolean skipLF = false; + + private boolean myIsProcessTerminated = false; + private final char[] myBuffer = new char[8192]; + + public ReadProcessThread(final Reader reader) { + myReader = reader; + } + + public synchronized void setProcessTerminated(boolean isProcessTerminated) { + myIsProcessTerminated = isProcessTerminated; + } + + public void run() { + try { + while (true) { + final int rc = readAvailable(); + if (rc == DONE) break; + Thread.sleep(rc == READ_SOME ? 1L : 50L); + } + } + catch (InterruptedException ignore) { + } + catch (Exception e) { + LOG.error(e); + } + } + + private static final int DONE = 0; + private static final int READ_SOME = 1; + private static final int READ_NONE = 2; + + private synchronized int readAvailable() throws IOException { + char[] buffer = myBuffer; + StringBuilder token = new StringBuilder(); + int rc = READ_NONE; + while (myReader.ready()) { + int n = myReader.read(buffer); + if (n <= 0) break; + rc = READ_SOME; + + for (int i = 0; i < n; i++) { + char c = buffer[i]; + if (skipLF && c != '\n') { + token.append('\r'); + } + + if (c == '\r') { + skipLF = true; + } + else { + skipLF = false; + token.append(c); + } + + if (c == '\n') { + textAvailable(token.toString()); + token.setLength(0); + } + } + } + + if (token.length() != 0) { + textAvailable(token.toString()); + token.setLength(0); + } + + if (myIsProcessTerminated) { + try { + myReader.close(); + } + catch (IOException e1) { + // supressed + } + + return DONE; + } + + return rc; + } + + protected abstract void textAvailable(final String s); + } +} diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index aed63e8974af..31c03b502121 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -1,303 +1,303 @@ -no.module.defined.error.message=No module defined -module.does.not.exist.error.message=Module ''{0}'' does not exist -no.jdk.for.module.error.message=No jdk for module ''{0}'' -jdk.is.bad.configured.error.message=''{0}'' is bad configured -class.not.found.in.module.error.message=Class ''{0}'' not found in module ''{1}'' -package.not.found.error.message=Package ''{0}'' not found -jdk.not.configured.error.message=Jdk ''{0}'' not configured -project.has.no.jdk.error.message=Project has no JDK -some.modules.has.circular.dependency.error.message=Some modules has circular dependency. -project.has.no.jdk.configured.error.message=Project has no JDK configured. -run.configuration.stop.action.name=Stop -warning.common.title=Warning -run.configuration.error.dialog.title=Run Configuration Error -no.jdk.specified..error.message=No JDK specified -home.directory.not.specified.for.jdk.error.message=Home directory is not specified for JDK -run.configuration.pause.output.action.name=Pause Output -main.class.is.not.specified.error.message=Main class is not specified -close.tab.action.name=Close -run.configuration.show.command.line.action.name=Show command line - -#--- -create.run.configuration.action.name=Create Run Configuration -create.run.configuration.for.item.action.name=Create {0} -create.run.configuration.for.item.dialog.title=Create Run/Debug Configuration: {0} -edit.configuration.action=&Edit Configurations -save.temporary.run.configuration.action.name=&Save ''{0}'' Configuration -choose.run.configuration.action.description=Open run/debug configurations dropdown - -#action - run. debug,profile etc -perform.action.with.context.configuration.action.name={0} context configuration - -error.common.title=Error -error.running.configuration.with.error.error.message=Error running {0}:
{1} - -select.applet.policy.file.dialog.title=Select applet policy file -choose.html.file.dialog.title=Choose HTML File -html.file.not.specified.error.message=Html file not specified -jre.not.valid.error.message=''{0}'' is not valid JRE home - -applet.configuration.description=Applet configuration -applet.configuration.name=Applet - -application.configuration.description=Application configuration -application.configuration.name=Application - -run.configuration.norunner.selected.label=No runner selected -run.configuration.configuration.tab.title=Configuration -run.configuration.startup.connection.rab.title=Startup/Connection - -add.new.run.configuration.acrtion.name=Add New Configuration -add.new.run.configuration.action.name=Add New ''{0}'' Configuration - -remove.run.configuration.action.name=Remove Configuration - -run.configuration.edit.default.configuration.settings.button=Edit De&faults -default.settings.editor.dialog.title=Default Settings -clear.all.from.console.action.name=Clear All -copy.selected.content.action.name=Copy Selected Content -copy.content.action.name=Copy Content -run.debug.dialog.title=Run/Debug Configurations -run.configuration.display.settings.checkbox=Display settings &before launching -run.configurable.display.name=Run -apply.action.name=&Apply -invalid.data.dialog.title=Invalid Data -template.settings.configurable.display.name=Template Settings -default.run.configuration.name= - -process.is.running.dialog.title=Process ''{0}'' is running -button.disconnect=Disconnect -disconnect.process.confirmation.text=Disconnect from the process ''{0}''? -terminate.after.disconnect.checkbox=Terminate the process after disconnect -copy.configuration.action.name=Copy Configuration -junit.configuration.display.name=JUnit -junit.configuration.description=JUnit test configuration -no.junit.error.message=No junit.jar -no.junit.in.scope.error.message=No junit.jar: {0} -junit.not.found.in.module.error.message=JUnit not found in module ''{0}'' -cannot.browse.test.inheritors.dialog.title=Can't Browse TestCase Inheritors -seaching.test.progress.title=Searching For Tests... -configuration.not.speficied.message=Configuration test type not specified -test.in.scope.presentable.text=Tests in ''{0}'' -all.tests.scope.presentable.text=All Tests -module.does.not.exists=Module ''{0}'' does not exist in project ''{1}'' -select.working.directory.message=Select working directory -set.class.name.message=Set class name first -cannot.browse.method.dialog.title=Cannot Browse Methods -class.does.not.exists.error.message=Class {0} does not exist -choose.package.dialog.title=Choose Package -choose.test.class.dialog.title=Choose Test Class -choose.test.method.dialog.title=Choose Test Method -test.cases.count.message={0} test case(s) -diff.content.expected.title=Expected -diff.content.expected.for.file.title=Expected : -diff.content.actual.title=Actual -junit.actual.text.label=Actual : -junit.click.to.see.diff.link= -output.tab.title=Output -statistics.tab.title=Statistics -test.not.started.progress.text=Tests were not started -starting.jvm.progress.text=Starting JVM... -instantiating.tests.progress.text=Instantiating tests... -next.faled.test.action.name=Next Failed Test -prev.faled.test.action.name=Previous Failed Test -junit.runing.info.memory.available.kb.message={0} kb. -junit.runing.info.memory.available.mb.message={0} Mb. -junit.runing.info.time.sec.message={0} s -junit.runing.info.total.label=Total: -junit.runing.info.starting.label=Starting... -junit.runing.info.running.label=Running -junit.runing.info.passed.label=Passed -junit.runing.info.terminated.label=Terminated -junit.runing.info.assertion.tree.node=Assertion -junit.runing.info.error.tree.node=Error -junit.runing.info.ignored.label=Ignored -junit.runing.info.ignored.console.message=Test ''{0}.{1}'' ignored -junit.runing.info.left.to.run.count.tree.node=Left: {0} -junit.runing.info.failed.count.message=F:{0} -junit.runing.info.errors.count.message=E:{0} -junit.runing.info.passed.count.message=P:{0} -junit.runing.info.ignored.count.message=I:{0} - -junit.runing.info.status.completed.from.total.failed={0} of {1} Failed: {2} -junit.runing.info.status.completed.from.total={0} of {1} -junit.runing.info.status.running.number.with.name=Running: {0} {1} -junit.runing.info.failed.to.start.error.message=Failed to start -junit.runing.info.tests.failed.label=Tests Failed -junit.runing.info.tests.passed.label=Tests Passed -tests.passed.with.warnings.message=Tests passed (with warnings) -junit.run.hide.passed.action.name=Hide Passed -junit.run.hide.passed.action.description=Hide passed tests -junit.runing.info.track.test.action.name=Track Running Test -junit.runing.info.track.test.action.description=Select currently running test in tree -junit.runing.info.collapse.test.action.name=Collapse all test suites -junit.runing.info.expand.test.action.name=Expand all test suites -junit.runing.info.select.first.failed.action.name=Select First Failed Test When Finished -junit.runing.info.scroll.to.stacktrace.action.name=Scroll to Stacktrace -junit.runing.info.scroll.to.stacktrace.action.description=Scroll console to beginning of assertion or exception stacktrace -junit.runing.info.open.source.at.exception.action.name=Open Source at Exception -junit.runing.info.open.source.at.exception.action.description=Go to line which caused exception when opening test source -junit.all.tests.passed.label=All Tests Passed -junit.tests.in.progress.label=Tests in Progress -junit.auto.scroll.to.source.action.name=Auto Scroll to Source -junit.open.text.in.editor.action.name=Open selected test in editor - -run.configuration.java.vm.parameters.label=&VM parameters: -run.configuration.program.parameters=Program pa&rameters: -run.configuration.working.directory.label=&Working directory: -run.configuration.use.alternate.jre.checkbox=Use alternative &JRE: -run.configuration.select.alternate.jre.label=Select Alternative JRE -run.configuration.select.jre.dir.label=Select directory with JRE to run with -run.configuration.arguments.help.panel.copy.action.name=Copy -terminating.process.progress.title=Terminating ''{0}'' -waiting.for.vm.detach.progress.text=Waiting for process detach -restart.error.message.title=Restart Error -rerun.configuration.action.name=Rerun {0} -run.configuration.dump.threads.action.name=Dump Threads -run.configuration.exit.action.name=Exit -run.error.message.title=Run Error -default.runner.start.action.text=R&un -remote.debug.configuration.description=Remote debug configuration -remote.debug.configuration.display.name=Remote - -applet.configuration.url.label=&URL -applet.configuration.applet.class.border=Applet Class -applet.configuration.applet.parameters.label=Applet Parameters -applet.configuration.applet.class.label=Applet &class: -button.remove=&Remove -button.add=&Add -applet.configuration.height.label= &Height: -applet.configuration.width.label= &Width: -applet.configuration.url.html.file.label=URL/HTML &file: -applet.configuration.url.border=URL -applet.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: -applet.configuration.vm.parameters.for.appletviewer.label=&VM parameters for appletviewer: -applet.configuration.policy.file.label=&Policy file: -applet.configuration.parameter.name.column=Name -applet.configuration.parameter.value.column=Value -class.not.specified.error.message=Class not specified. -failed.to.generate.wrapper.error.message=Failed to generate temporary html wrapper for applet class - -application.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: -application.configuration.main.class.label=Main &class: -jre.path.is.not.valid.jre.home.error.mesage=''{0}'' is not valid JRE home -main.method.not.found.in.class.error.message=Main method not found in class {0} -no.user.process.input.error.message=No process input -fix.run.configuration.problem.button=Fix -class.isnt.test.class.error.message={0} isn''t test class -class.isnt.inheritor.of.testcase.error.message={0} isn''t inheritor of TestCase -junit.jar.not.found.in.module.class.path.error.message=junit.jar not found in module ''{0}'' class path. -method.name.not.specified.error.message=Method name not specified -test.method.doesnt.exist.error.message=Test method ''{0}'' doesn''t exist -no.tests.found.in.package.error.message=No tests found in the package ''{0}'' -package.does.not.exist.error.message=Package ''{0}'' does not exist -choose.main.class.dialog.title=Choose Main Class -choose.applet.class.dialog.title=Choose Applet Class -junit.configuration.test.runner.parameters.label=Test runner pa&rameters: -junit.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: -junit.configuration.test.border=Test -junit.configuration.across.module.dependencies.radio=Across modu&le dependencies -junit.configuration.in.single.module.radio=In s&ingle module -junit.configuration.in.whole.project.radio=In &whole project -junit.configuration.search.for.tests.label=Search for tests: -junit.configuration.package.label=Packa&ge: -junit.configuration.method.label=Mðod: -junit.configuration.class.label=&Class: -junit.configuration.configure.junit.test.label=Test: -junit.configuration.test.method.radio=Test Method -junit.configuration.test.class.radio=Test Class -junit.configuration.class.radio=C&lass -junit.configuration.method.radio=Me&thod -jnit.configuration.all.tests.in.package.radio=All Tests in Package -junit.configuration.all.in.package.radio=All in &Package -no.jdk.specified.for.module.warning.text=No JDK specified for module ''{0}'' -module.not.specified.error.text=Module not specified -module.doesn.t.exist.in.project.error.text=Module ''{0}'' doesn''t exist in project -run.configuration.unnamed.name.prefix=Unnamed -no.applet.class.specified.error.message=No applet class specified -no.main.class.specified.error.text=No main class specified -action.name.save.configuration=Save Configuration -#2 - configuration type description -empty.run.configuration.panel.text.label=

Press the \\  button \ - to create a new {3} based on default settings. -default.package.presentable.name= -default.package.configuration.name=default package -no.test.class.specified.error.text=No test class specified -edit.run.configuration.run.configuration.name.label=&Name: -default.junit.configuration.name= -strings.equal.failed.dialog.title=Comparison failure -junit.runing.info.test.column.name=Test -junit.runing.info.time.elapsed.column.name=Time elapsed -junit.runing.info.usage.delta.column.name=Usage Delta -junit.runing.info.usage.before.column.name=Usage Before -junit.runing.info.usage.after.column.name=Usage After -junit.runing.info.results.column.name=Results -junit.runing.info.loading.tree.node.text=loading -remote.configuration.settings.border=Settings -remote.configuration.transport.label=Transport: -remote.configuration.listen.radio=Listen -remote.configuration.attach.radio=Attach -remote.configuration.debugger.mode.label=Debugger mode: -remote.configuration.shared.memory.radio=Shared memory -remote.configuration.socket.radio=Socket -remote.configuration.shared.memory.address.label=Shared memory address: -remote.configuration.port.label=Port: -remote.configuration.host.label=Host: -remote.configuration.remote.debugging.allows.you.to.connect.idea.to.a.running.jvm.label=Remote debugging allows you to connect IDEA to a running JVM. -standard.runner.description=Run selected configuration -environment.variables.helper.use.arguments.label=Use the following command line arguments for running remote JVM (you may copy and paste them) -environment.variables.helper.use.arguments.jdk13.label=If the application runs on JDK 1.3.x or earlier, use following arguments -select.run.configuration.for.item.action.name=Select {0} -save.run.configuration.for.item.action.name=Save {0} -junit.runing.info.status.done.count=Done: {0} -junit.runing.info.status.terminated.count=Terminated: {0} -junit.runing.info.tests.in.progress.done.tree.node=Tests in Progress: Done -junit.runing.info.tests.in.progress.terminated.tre.node=Tests in Progress: Terminated -delete.confirmation.dialog.title=Delete Confirmation -move.up.action.name=Move Up -move.down.action.name=Move Down -memory.available.message={0} Kb - -#code coverage -enable.coverage.with.emma=Record code &coverage information -merge.coverage.data=&Merge gathered coverage with suite chosen below -record.coverage.filters.title=Packages and classes to record coverage data -coverage.tab.title=Code Coverage -show.swing.inspector=&Enable capturing form snapshots -show.swing.inspector.disabled=&Enable capturing form snapshots (requires JRE 5.0 or higher) -before.run.property.make=Make -run.configuration.store.place.option=&Share configuration -run.configuration.default.type.description=configuration - -#GeneralCommandLine -run.configuration.error.no.jdk.specified=No JDK specified -run.configuration.cannot.find.vm.executable=Cannot find VM executable - -logs.tab.title=Logs -before.launch.panel.title=Before launch -action.name.save.as.configuration=Save As -default.junit.config.name.all.in.module=All in {0} -default.junit.config.name.all.in.package.in.module={0} in {1} -environment.variables.dialog.title=Environment Variables -environment.variables.component.title=&Environment Variables -down.the.stack.trace=Down the stack trace -up.the.stack.trace=Up the stack trace -configuration.action.chooser.title=Choose configuration type to run -env.vars.checkbox.title=&Include parent environment variables - -before.launch.compile.step=Make -execute.before.run.debug.dialog.title=Execute {0} Before Run/Debug - -export.test.results.filename=Test Results - {0} -export.test.results.succeeded=Test results exported successfully to {0} -export.test.results.failed=Test results export failed: {0} -export.test.results.custom.template.chooser.title=Choose Custom Template -export.test.results.output.folder.chooser.title=Choose Output Folder -export.test.results.custom.template.path.empty=User-defined tempate path is empty -export.test.results.custom.template.not.found=User-defined tempate file ''{0}'' is not found -export.test.results.task.name=Exporting test results -export.test.results.open.editor=O&pen exported file in editor -export.test.results.open.browser=O&pen exported file in browser -export.test.results.dialog.title=Export Test Results -export.test.results.output.path.empty=Output path is empty -export.test.results.output.filename.empty=Output file name is empty +no.module.defined.error.message=No module defined +module.does.not.exist.error.message=Module ''{0}'' does not exist +no.jdk.for.module.error.message=No jdk for module ''{0}'' +jdk.is.bad.configured.error.message=''{0}'' is bad configured +class.not.found.in.module.error.message=Class ''{0}'' not found in module ''{1}'' +package.not.found.error.message=Package ''{0}'' not found +jdk.not.configured.error.message=Jdk ''{0}'' not configured +project.has.no.jdk.error.message=Project has no JDK +some.modules.has.circular.dependency.error.message=Some modules has circular dependency. +project.has.no.jdk.configured.error.message=Project has no JDK configured. +run.configuration.stop.action.name=Stop +warning.common.title=Warning +run.configuration.error.dialog.title=Run Configuration Error +no.jdk.specified..error.message=No JDK specified +home.directory.not.specified.for.jdk.error.message=Home directory is not specified for JDK +run.configuration.pause.output.action.name=Pause Output +main.class.is.not.specified.error.message=Main class is not specified +close.tab.action.name=Close +run.configuration.show.command.line.action.name=Show command line + +#--- +create.run.configuration.action.name=Create Run Configuration +create.run.configuration.for.item.action.name=Create {0} +create.run.configuration.for.item.dialog.title=Create Run/Debug Configuration: {0} +edit.configuration.action=&Edit Configurations +save.temporary.run.configuration.action.name=&Save ''{0}'' Configuration +choose.run.configuration.action.description=Open run/debug configurations dropdown + +#action - run. debug,profile etc +perform.action.with.context.configuration.action.name={0} context configuration + +error.common.title=Error +error.running.configuration.with.error.error.message=Error running {0}:
{1} + +select.applet.policy.file.dialog.title=Select applet policy file +choose.html.file.dialog.title=Choose HTML File +html.file.not.specified.error.message=Html file not specified +jre.not.valid.error.message=''{0}'' is not valid JRE home + +applet.configuration.description=Applet configuration +applet.configuration.name=Applet + +application.configuration.description=Application configuration +application.configuration.name=Application + +run.configuration.norunner.selected.label=No runner selected +run.configuration.configuration.tab.title=Configuration +run.configuration.startup.connection.rab.title=Startup/Connection + +add.new.run.configuration.acrtion.name=Add New Configuration +add.new.run.configuration.action.name=Add New ''{0}'' Configuration + +remove.run.configuration.action.name=Remove Configuration + +run.configuration.edit.default.configuration.settings.button=Edit De&faults +default.settings.editor.dialog.title=Default Settings +clear.all.from.console.action.name=Clear All +copy.selected.content.action.name=Copy Selected Content +copy.content.action.name=Copy Content +run.debug.dialog.title=Run/Debug Configurations +run.configuration.display.settings.checkbox=Display settings &before launching +run.configurable.display.name=Run +apply.action.name=&Apply +invalid.data.dialog.title=Invalid Data +template.settings.configurable.display.name=Template Settings +default.run.configuration.name= + +process.is.running.dialog.title=Process ''{0}'' is running +button.disconnect=Disconnect +disconnect.process.confirmation.text=Disconnect from the process ''{0}''? +terminate.after.disconnect.checkbox=Terminate the process after disconnect +copy.configuration.action.name=Copy Configuration +junit.configuration.display.name=JUnit +junit.configuration.description=JUnit test configuration +no.junit.error.message=No junit.jar +no.junit.in.scope.error.message=No junit.jar: {0} +junit.not.found.in.module.error.message=JUnit not found in module ''{0}'' +cannot.browse.test.inheritors.dialog.title=Can't Browse TestCase Inheritors +seaching.test.progress.title=Searching For Tests... +configuration.not.speficied.message=Configuration test type not specified +test.in.scope.presentable.text=Tests in ''{0}'' +all.tests.scope.presentable.text=All Tests +module.does.not.exists=Module ''{0}'' does not exist in project ''{1}'' +select.working.directory.message=Select working directory +set.class.name.message=Set class name first +cannot.browse.method.dialog.title=Cannot Browse Methods +class.does.not.exists.error.message=Class {0} does not exist +choose.package.dialog.title=Choose Package +choose.test.class.dialog.title=Choose Test Class +choose.test.method.dialog.title=Choose Test Method +test.cases.count.message={0} test case(s) +diff.content.expected.title=Expected +diff.content.expected.for.file.title=Expected : +diff.content.actual.title=Actual +junit.actual.text.label=Actual : +junit.click.to.see.diff.link= +output.tab.title=Output +statistics.tab.title=Statistics +test.not.started.progress.text=Tests were not started +starting.jvm.progress.text=Starting JVM... +instantiating.tests.progress.text=Instantiating tests... +next.faled.test.action.name=Next Failed Test +prev.faled.test.action.name=Previous Failed Test +junit.runing.info.memory.available.kb.message={0} kb. +junit.runing.info.memory.available.mb.message={0} Mb. +junit.runing.info.time.sec.message={0} s +junit.runing.info.total.label=Total: +junit.runing.info.starting.label=Starting... +junit.runing.info.running.label=Running +junit.runing.info.passed.label=Passed +junit.runing.info.terminated.label=Terminated +junit.runing.info.assertion.tree.node=Assertion +junit.runing.info.error.tree.node=Error +junit.runing.info.ignored.label=Ignored +junit.runing.info.ignored.console.message=Test ''{0}.{1}'' ignored +junit.runing.info.left.to.run.count.tree.node=Left: {0} +junit.runing.info.failed.count.message=F:{0} +junit.runing.info.errors.count.message=E:{0} +junit.runing.info.passed.count.message=P:{0} +junit.runing.info.ignored.count.message=I:{0} + +junit.runing.info.status.completed.from.total.failed={0} of {1} Failed: {2} +junit.runing.info.status.completed.from.total={0} of {1} +junit.runing.info.status.running.number.with.name=Running: {0} {1} +junit.runing.info.failed.to.start.error.message=Failed to start +junit.runing.info.tests.failed.label=Tests Failed +junit.runing.info.tests.passed.label=Tests Passed +tests.passed.with.warnings.message=Tests passed (with warnings) +junit.run.hide.passed.action.name=Hide Passed +junit.run.hide.passed.action.description=Hide passed tests +junit.runing.info.track.test.action.name=Track Running Test +junit.runing.info.track.test.action.description=Select currently running test in tree +junit.runing.info.collapse.test.action.name=Collapse all test suites +junit.runing.info.expand.test.action.name=Expand all test suites +junit.runing.info.select.first.failed.action.name=Select First Failed Test When Finished +junit.runing.info.scroll.to.stacktrace.action.name=Scroll to Stacktrace +junit.runing.info.scroll.to.stacktrace.action.description=Scroll console to beginning of assertion or exception stacktrace +junit.runing.info.open.source.at.exception.action.name=Open Source at Exception +junit.runing.info.open.source.at.exception.action.description=Go to line which caused exception when opening test source +junit.all.tests.passed.label=All Tests Passed +junit.tests.in.progress.label=Tests in Progress +junit.auto.scroll.to.source.action.name=Auto Scroll to Source +junit.open.text.in.editor.action.name=Open selected test in editor + +run.configuration.java.vm.parameters.label=&VM parameters: +run.configuration.program.parameters=Program pa&rameters: +run.configuration.working.directory.label=&Working directory: +run.configuration.use.alternate.jre.checkbox=Use alternative &JRE: +run.configuration.select.alternate.jre.label=Select Alternative JRE +run.configuration.select.jre.dir.label=Select directory with JRE to run with +run.configuration.arguments.help.panel.copy.action.name=Copy +terminating.process.progress.title=Terminating ''{0}'' +waiting.for.vm.detach.progress.text=Waiting for process detach +restart.error.message.title=Restart Error +rerun.configuration.action.name=Rerun {0} +run.configuration.dump.threads.action.name=Dump Threads +run.configuration.exit.action.name=Exit +run.error.message.title=Run Error +default.runner.start.action.text=R&un +remote.debug.configuration.description=Remote debug configuration +remote.debug.configuration.display.name=Remote + +applet.configuration.url.label=&URL +applet.configuration.applet.class.border=Applet Class +applet.configuration.applet.parameters.label=Applet Parameters +applet.configuration.applet.class.label=Applet &class: +button.remove=&Remove +button.add=&Add +applet.configuration.height.label= &Height: +applet.configuration.width.label= &Width: +applet.configuration.url.html.file.label=URL/HTML &file: +applet.configuration.url.border=URL +applet.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: +applet.configuration.vm.parameters.for.appletviewer.label=&VM parameters for appletviewer: +applet.configuration.policy.file.label=&Policy file: +applet.configuration.parameter.name.column=Name +applet.configuration.parameter.value.column=Value +class.not.specified.error.message=Class not specified. +failed.to.generate.wrapper.error.message=Failed to generate temporary html wrapper for applet class + +application.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: +application.configuration.main.class.label=Main &class: +jre.path.is.not.valid.jre.home.error.mesage=''{0}'' is not valid JRE home +main.method.not.found.in.class.error.message=Main method not found in class {0} +no.user.process.input.error.message=No process input +fix.run.configuration.problem.button=Fix +class.isnt.test.class.error.message={0} isn''t test class +class.isnt.inheritor.of.testcase.error.message={0} isn''t inheritor of TestCase +junit.jar.not.found.in.module.class.path.error.message=junit.jar not found in module ''{0}'' class path. +method.name.not.specified.error.message=Method name not specified +test.method.doesnt.exist.error.message=Test method ''{0}'' doesn''t exist +no.tests.found.in.package.error.message=No tests found in the package ''{0}'' +package.does.not.exist.error.message=Package ''{0}'' does not exist +choose.main.class.dialog.title=Choose Main Class +choose.applet.class.dialog.title=Choose Applet Class +junit.configuration.test.runner.parameters.label=Test runner pa&rameters: +junit.configuration.use.classpath.and.jdk.of.module.label=Use classpath and JDK of m&odule: +junit.configuration.test.border=Test +junit.configuration.across.module.dependencies.radio=Across modu&le dependencies +junit.configuration.in.single.module.radio=In s&ingle module +junit.configuration.in.whole.project.radio=In &whole project +junit.configuration.search.for.tests.label=Search for tests: +junit.configuration.package.label=Packa&ge: +junit.configuration.method.label=Mðod: +junit.configuration.class.label=&Class: +junit.configuration.configure.junit.test.label=Test: +junit.configuration.test.method.radio=Test Method +junit.configuration.test.class.radio=Test Class +junit.configuration.class.radio=C&lass +junit.configuration.method.radio=Me&thod +jnit.configuration.all.tests.in.package.radio=All Tests in Package +junit.configuration.all.in.package.radio=All in &Package +no.jdk.specified.for.module.warning.text=No JDK specified for module ''{0}'' +module.not.specified.error.text=Module not specified +module.doesn.t.exist.in.project.error.text=Module ''{0}'' doesn''t exist in project +run.configuration.unnamed.name.prefix=Unnamed +no.applet.class.specified.error.message=No applet class specified +no.main.class.specified.error.text=No main class specified +action.name.save.configuration=Save Configuration +#2 - configuration type description +empty.run.configuration.panel.text.label=

Press the \\  button \ + to create a new {3} based on default settings. +default.package.presentable.name= +default.package.configuration.name=default package +no.test.class.specified.error.text=No test class specified +edit.run.configuration.run.configuration.name.label=&Name: +default.junit.configuration.name= +strings.equal.failed.dialog.title=Comparison failure +junit.runing.info.test.column.name=Test +junit.runing.info.time.elapsed.column.name=Time elapsed +junit.runing.info.usage.delta.column.name=Usage Delta +junit.runing.info.usage.before.column.name=Usage Before +junit.runing.info.usage.after.column.name=Usage After +junit.runing.info.results.column.name=Results +junit.runing.info.loading.tree.node.text=loading +remote.configuration.settings.border=Settings +remote.configuration.transport.label=Transport: +remote.configuration.listen.radio=Listen +remote.configuration.attach.radio=Attach +remote.configuration.debugger.mode.label=Debugger mode: +remote.configuration.shared.memory.radio=Shared memory +remote.configuration.socket.radio=Socket +remote.configuration.shared.memory.address.label=Shared memory address: +remote.configuration.port.label=Port: +remote.configuration.host.label=Host: +remote.configuration.remote.debugging.allows.you.to.connect.idea.to.a.running.jvm.label=Remote debugging allows you to connect IDEA to a running JVM. +standard.runner.description=Run selected configuration +environment.variables.helper.use.arguments.label=Use the following command line arguments for running remote JVM (you may copy and paste them) +environment.variables.helper.use.arguments.jdk13.label=If the application runs on JDK 1.3.x or earlier, use following arguments +select.run.configuration.for.item.action.name=Select {0} +save.run.configuration.for.item.action.name=Save {0} +junit.runing.info.status.done.count=Done: {0} +junit.runing.info.status.terminated.count=Terminated: {0} +junit.runing.info.tests.in.progress.done.tree.node=Tests in Progress: Done +junit.runing.info.tests.in.progress.terminated.tre.node=Tests in Progress: Terminated +delete.confirmation.dialog.title=Delete Confirmation +move.up.action.name=Move Up +move.down.action.name=Move Down +memory.available.message={0} Kb + +#code coverage +enable.coverage.with.emma=Record code &coverage information +merge.coverage.data=&Merge gathered coverage with suite chosen below +record.coverage.filters.title=Packages and classes to record coverage data +coverage.tab.title=Code Coverage +show.swing.inspector=&Enable capturing form snapshots +show.swing.inspector.disabled=&Enable capturing form snapshots (requires JRE 5.0 or higher) +before.run.property.make=Make +run.configuration.store.place.option=&Share configuration +run.configuration.default.type.description=configuration + +#GeneralCommandLine +run.configuration.error.no.jdk.specified=No JDK specified +run.configuration.cannot.find.vm.executable=Cannot find VM executable + +logs.tab.title=Logs +before.launch.panel.title=Before launch +action.name.save.as.configuration=Save As +default.junit.config.name.all.in.module=All in {0} +default.junit.config.name.all.in.package.in.module={0} in {1} +environment.variables.dialog.title=Environment Variables +environment.variables.component.title=&Environment Variables +down.the.stack.trace=Down the stack trace +up.the.stack.trace=Up the stack trace +configuration.action.chooser.title=Choose configuration type to run +env.vars.checkbox.title=&Include parent environment variables + +before.launch.compile.step=Make +execute.before.run.debug.dialog.title=Execute {0} Before Run/Debug + +export.test.results.filename=Test Results - {0} +export.test.results.succeeded=Test results exported successfully to {0} +export.test.results.failed=Test results export failed: {0} +export.test.results.custom.template.chooser.title=Choose Custom Template +export.test.results.output.folder.chooser.title=Choose Output Folder +export.test.results.custom.template.path.empty=User-defined tempate path is empty +export.test.results.custom.template.not.found=User-defined tempate file ''{0}'' is not found +export.test.results.task.name=Exporting test results +export.test.results.open.editor=O&pen exported file in editor +export.test.results.open.browser=O&pen exported file in browser +export.test.results.dialog.title=Export Test Results +export.test.results.output.path.empty=Output path is empty +export.test.results.output.filename.empty=Output file name is empty diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java index 0f8252f0e3e2..d9f736e3d534 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java @@ -1,256 +1,256 @@ -/* - * Copyright 2000-2009 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.xdebugger.impl.ui; - -import com.intellij.debugger.ui.DebuggerContentInfo; -import com.intellij.execution.DefaultExecutionResult; -import com.intellij.execution.ExecutionResult; -import com.intellij.execution.Executor; -import com.intellij.execution.configurations.RunProfile; -import com.intellij.execution.executors.DefaultDebugExecutor; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.execution.runners.ProgramRunner; -import com.intellij.execution.runners.RestartAction; -import com.intellij.execution.runners.RunContentBuilder; -import com.intellij.execution.ui.*; -import com.intellij.execution.ui.actions.CloseAction; -import com.intellij.execution.ui.layout.PlaceInGrid; -import com.intellij.ide.CommonActionsManager; -import com.intellij.ide.actions.ContextHelpAction; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.ui.content.Content; -import com.intellij.ui.content.tabs.PinToolwindowTabAction; -import com.intellij.util.ArrayUtil; -import com.intellij.xdebugger.XDebugProcess; -import com.intellij.xdebugger.XDebugSession; -import com.intellij.xdebugger.XDebuggerBundle; -import com.intellij.xdebugger.impl.XDebugSessionImpl; -import com.intellij.xdebugger.impl.actions.XDebuggerActions; -import com.intellij.xdebugger.impl.frame.XDebugViewBase; -import com.intellij.xdebugger.impl.frame.XFramesView; -import com.intellij.xdebugger.impl.frame.XVariablesView; -import com.intellij.xdebugger.impl.frame.XWatchesView; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * @author spleaner - */ -public class XDebugSessionTab extends DebuggerSessionTabBase { - private final String mySessionName; - private final RunnerLayoutUi myUi; - private XWatchesView myWatchesView; - private final List myViews = new ArrayList(); - - public XDebugSessionTab(@NotNull final Project project, @NotNull final String sessionName) { - super(project); - mySessionName = sessionName; - - myUi = RunnerLayoutUi.Factory.getInstance(project).create("Debug", "unknown!", sessionName, this); - myUi.getDefaults().initTabDefaults(0, "Debug", null); - - myUi.getOptions().setTopToolbar(createTopToolbar(), ActionPlaces.DEBUGGER_TOOLBAR); - } - - private Content createConsoleContent() { - return myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(), - XDebuggerBundle.message("debugger.session.tab.console.content.name"), XDebuggerUIConstants.CONSOLE_TAB_ICON, - myConsole.getPreferredFocusableComponent()); - } - - private Content createVariablesContent(final XDebugSession session) { - final XVariablesView variablesView = new XVariablesView(session, this); - myViews.add(variablesView); - return myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, variablesView.getPanel(), - XDebuggerBundle.message("debugger.session.tab.variables.title"), XDebuggerUIConstants.VARIABLES_TAB_ICON, null); - } - - private Content createWatchesContent(final XDebugSession session, final XDebugSessionData sessionData) { - myWatchesView = new XWatchesView(session, this, sessionData); - myViews.add(myWatchesView); - Content watchesContent = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchesView.getMainPanel(), - XDebuggerBundle.message("debugger.session.tab.watches.title"), XDebuggerUIConstants.WATCHES_TAB_ICON, null); - - ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(XDebuggerActions.WATCHES_TREE_TOOLBAR_GROUP); - watchesContent.setActions(group, ActionPlaces.DEBUGGER_TOOLBAR, myWatchesView.getTree()); - return watchesContent; - } - - private Content createFramesContent(final XDebugSession session) { - final XFramesView framesView = new XFramesView(session, this); - myViews.add(framesView); - Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, framesView.getMainPanel(), - XDebuggerBundle.message("debugger.session.tab.frames.title"), XDebuggerUIConstants.FRAMES_TAB_ICON, null); - final DefaultActionGroup framesGroup = new DefaultActionGroup(); - - CommonActionsManager actionsManager = CommonActionsManager.getInstance(); - framesGroup.add(actionsManager.createPrevOccurenceAction(framesView.getFramesList())); - framesGroup.add(actionsManager.createNextOccurenceAction(framesView.getFramesList())); - - framesContent.setActions(framesGroup, ActionPlaces.DEBUGGER_TOOLBAR, framesView.getFramesList()); - return framesContent; - } - - private static DefaultActionGroup createTopToolbar() { - DefaultActionGroup stepping = new DefaultActionGroup(); - ActionManager actionManager = ActionManager.getInstance(); - stepping.add(actionManager.getAction(XDebuggerActions.SHOW_EXECUTION_POINT)); - stepping.addSeparator(); - stepping.add(actionManager.getAction(XDebuggerActions.STEP_OVER)); - stepping.add(actionManager.getAction(XDebuggerActions.STEP_INTO)); - stepping.add(actionManager.getAction(XDebuggerActions.FORCE_STEP_INTO)); - stepping.add(actionManager.getAction(XDebuggerActions.STEP_OUT)); - stepping.addSeparator(); - stepping.add(actionManager.getAction(XDebuggerActions.RUN_TO_CURSOR)); - return stepping; - } - - public XDebugSessionData saveData() { - final List watchExpressions = myWatchesView.getWatchExpressions(); - return new XDebugSessionData(ArrayUtil.toStringArray(watchExpressions)); - } - - public ExecutionConsole getConsole() { - return myConsole; - } - - public String getSessionName() { - return mySessionName; - } - - public void rebuildViews() { - for (XDebugViewBase view : myViews) { - view.rebuildView(); - } - } - - public RunContentDescriptor attachToSession(final @NotNull XDebugSession session, final @Nullable ProgramRunner runner, - final @Nullable ExecutionEnvironment env, - final @NotNull XDebugSessionData sessionData) { - return initUI(session, sessionData, env, runner); - } - - @NotNull - private static ExecutionResult createExecutionResult(@NotNull final XDebugSession session) { - final XDebugProcess debugProcess = session.getDebugProcess(); - ProcessHandler processHandler = debugProcess.getProcessHandler(); - processHandler.addProcessListener(new ProcessAdapter() { - public void processTerminated(final ProcessEvent event) { - ((XDebugSessionImpl)session).stopImpl(); - } - }); - return new DefaultExecutionResult(debugProcess.createConsole(), processHandler); - } - - public XWatchesView getWatchesView() { - return myWatchesView; - } - - private RunContentDescriptor initUI(final @NotNull XDebugSession session, final @NotNull XDebugSessionData sessionData, - final @Nullable ExecutionEnvironment environment, final @Nullable ProgramRunner runner) { - ExecutionResult executionResult = createExecutionResult(session); - myConsole = executionResult.getExecutionConsole(); - myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName()); - - myUi.addContent(createFramesContent(session), 0, PlaceInGrid.left, false); - myUi.addContent(createVariablesContent(session), 0, PlaceInGrid.center, false); - myUi.addContent(createWatchesContent(session, sessionData), 0, PlaceInGrid.right, false); - final Content consoleContent = createConsoleContent(); - myUi.addContent(consoleContent, 1, PlaceInGrid.bottom, false); - if (myConsole instanceof ObservableConsoleView) { - ObservableConsoleView observable = (ObservableConsoleView)myConsole; - observable.addChangeListener(new ObservableConsoleView.ChangeListener() { - public void contentAdded(final Collection types) { - if (types.contains(ConsoleViewContentType.ERROR_OUTPUT) || types.contains(ConsoleViewContentType.SYSTEM_OUTPUT)) { - consoleContent.fireAlert(); - } - } - }, consoleContent); - } - session.getDebugProcess().registerAdditionalContent(myUi); - RunContentBuilder.addAdditionalConsoleEditorActions(myConsole, consoleContent); - myUi.addContent(consoleContent, 0, PlaceInGrid.bottom, false); - - if (ApplicationManager.getApplication().isUnitTestMode()) { - return myRunContentDescriptor; - } - - DefaultActionGroup group = new DefaultActionGroup(); - final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); - if (runner != null && environment != null) { - RestartAction restartAction = new RestartAction(executor, runner, myRunContentDescriptor.getProcessHandler(), XDebuggerUIConstants.DEBUG_AGAIN_ICON, - myRunContentDescriptor, environment); - group.add(restartAction); - restartAction.registerShortcut(myUi.getComponent()); - } - - addActionToGroup(group, XDebuggerActions.RESUME); - addActionToGroup(group, XDebuggerActions.PAUSE); - addActionToGroup(group, IdeActions.ACTION_STOP_PROGRAM); - - group.addSeparator(); - - addActionToGroup(group, XDebuggerActions.VIEW_BREAKPOINTS); - addActionToGroup(group, XDebuggerActions.MUTE_BREAKPOINTS); - - group.addSeparator(); - //addAction(group, DebuggerActions.EXPORT_THREADS); - group.addSeparator(); - - group.add(myUi.getOptions().getLayoutActions()); - - group.addSeparator(); - - group.add(PinToolwindowTabAction.getPinAction()); - group.add(new CloseAction(executor, myRunContentDescriptor, getProject())); - group.add(new ContextHelpAction(executor.getHelpId())); - - myUi.getOptions().setLeftToolbar(group, ActionPlaces.DEBUGGER_TOOLBAR); - - if (environment != null) { - final RunProfile runConfiguration = environment.getRunProfile(); - registerFileMatcher(runConfiguration); - initLogConsoles(runConfiguration, myRunContentDescriptor.getProcessHandler()); - } - - rebuildViews(); - - return myRunContentDescriptor; - } - - private static void addActionToGroup(final DefaultActionGroup group, final String actionId) { - AnAction action = ActionManager.getInstance().getAction(actionId); - if (action != null) group.add(action); - } - - public RunnerLayoutUi getUi() { - return myUi; - } - - @Nullable - public RunContentDescriptor getRunContentDescriptor() { - return myRunContentDescriptor; - } +/* + * Copyright 2000-2009 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.xdebugger.impl.ui; + +import com.intellij.debugger.ui.DebuggerContentInfo; +import com.intellij.execution.DefaultExecutionResult; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.runners.RestartAction; +import com.intellij.execution.runners.RunContentBuilder; +import com.intellij.execution.ui.*; +import com.intellij.execution.ui.actions.CloseAction; +import com.intellij.execution.ui.layout.PlaceInGrid; +import com.intellij.ide.CommonActionsManager; +import com.intellij.ide.actions.ContextHelpAction; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.tabs.PinToolwindowTabAction; +import com.intellij.util.ArrayUtil; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerBundle; +import com.intellij.xdebugger.impl.XDebugSessionImpl; +import com.intellij.xdebugger.impl.actions.XDebuggerActions; +import com.intellij.xdebugger.impl.frame.XDebugViewBase; +import com.intellij.xdebugger.impl.frame.XFramesView; +import com.intellij.xdebugger.impl.frame.XVariablesView; +import com.intellij.xdebugger.impl.frame.XWatchesView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author spleaner + */ +public class XDebugSessionTab extends DebuggerSessionTabBase { + private final String mySessionName; + private final RunnerLayoutUi myUi; + private XWatchesView myWatchesView; + private final List myViews = new ArrayList(); + + public XDebugSessionTab(@NotNull final Project project, @NotNull final String sessionName) { + super(project); + mySessionName = sessionName; + + myUi = RunnerLayoutUi.Factory.getInstance(project).create("Debug", "unknown!", sessionName, this); + myUi.getDefaults().initTabDefaults(0, "Debug", null); + + myUi.getOptions().setTopToolbar(createTopToolbar(), ActionPlaces.DEBUGGER_TOOLBAR); + } + + private Content createConsoleContent() { + return myUi.createContent(DebuggerContentInfo.CONSOLE_CONTENT, myConsole.getComponent(), + XDebuggerBundle.message("debugger.session.tab.console.content.name"), XDebuggerUIConstants.CONSOLE_TAB_ICON, + myConsole.getPreferredFocusableComponent()); + } + + private Content createVariablesContent(final XDebugSession session) { + final XVariablesView variablesView = new XVariablesView(session, this); + myViews.add(variablesView); + return myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, variablesView.getPanel(), + XDebuggerBundle.message("debugger.session.tab.variables.title"), XDebuggerUIConstants.VARIABLES_TAB_ICON, null); + } + + private Content createWatchesContent(final XDebugSession session, final XDebugSessionData sessionData) { + myWatchesView = new XWatchesView(session, this, sessionData); + myViews.add(myWatchesView); + Content watchesContent = myUi.createContent(DebuggerContentInfo.WATCHES_CONTENT, myWatchesView.getMainPanel(), + XDebuggerBundle.message("debugger.session.tab.watches.title"), XDebuggerUIConstants.WATCHES_TAB_ICON, null); + + ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(XDebuggerActions.WATCHES_TREE_TOOLBAR_GROUP); + watchesContent.setActions(group, ActionPlaces.DEBUGGER_TOOLBAR, myWatchesView.getTree()); + return watchesContent; + } + + private Content createFramesContent(final XDebugSession session) { + final XFramesView framesView = new XFramesView(session, this); + myViews.add(framesView); + Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, framesView.getMainPanel(), + XDebuggerBundle.message("debugger.session.tab.frames.title"), XDebuggerUIConstants.FRAMES_TAB_ICON, null); + final DefaultActionGroup framesGroup = new DefaultActionGroup(); + + CommonActionsManager actionsManager = CommonActionsManager.getInstance(); + framesGroup.add(actionsManager.createPrevOccurenceAction(framesView.getFramesList())); + framesGroup.add(actionsManager.createNextOccurenceAction(framesView.getFramesList())); + + framesContent.setActions(framesGroup, ActionPlaces.DEBUGGER_TOOLBAR, framesView.getFramesList()); + return framesContent; + } + + private static DefaultActionGroup createTopToolbar() { + DefaultActionGroup stepping = new DefaultActionGroup(); + ActionManager actionManager = ActionManager.getInstance(); + stepping.add(actionManager.getAction(XDebuggerActions.SHOW_EXECUTION_POINT)); + stepping.addSeparator(); + stepping.add(actionManager.getAction(XDebuggerActions.STEP_OVER)); + stepping.add(actionManager.getAction(XDebuggerActions.STEP_INTO)); + stepping.add(actionManager.getAction(XDebuggerActions.FORCE_STEP_INTO)); + stepping.add(actionManager.getAction(XDebuggerActions.STEP_OUT)); + stepping.addSeparator(); + stepping.add(actionManager.getAction(XDebuggerActions.RUN_TO_CURSOR)); + return stepping; + } + + public XDebugSessionData saveData() { + final List watchExpressions = myWatchesView.getWatchExpressions(); + return new XDebugSessionData(ArrayUtil.toStringArray(watchExpressions)); + } + + public ExecutionConsole getConsole() { + return myConsole; + } + + public String getSessionName() { + return mySessionName; + } + + public void rebuildViews() { + for (XDebugViewBase view : myViews) { + view.rebuildView(); + } + } + + public RunContentDescriptor attachToSession(final @NotNull XDebugSession session, final @Nullable ProgramRunner runner, + final @Nullable ExecutionEnvironment env, + final @NotNull XDebugSessionData sessionData) { + return initUI(session, sessionData, env, runner); + } + + @NotNull + private static ExecutionResult createExecutionResult(@NotNull final XDebugSession session) { + final XDebugProcess debugProcess = session.getDebugProcess(); + ProcessHandler processHandler = debugProcess.getProcessHandler(); + processHandler.addProcessListener(new ProcessAdapter() { + public void processTerminated(final ProcessEvent event) { + ((XDebugSessionImpl)session).stopImpl(); + } + }); + return new DefaultExecutionResult(debugProcess.createConsole(), processHandler); + } + + public XWatchesView getWatchesView() { + return myWatchesView; + } + + private RunContentDescriptor initUI(final @NotNull XDebugSession session, final @NotNull XDebugSessionData sessionData, + final @Nullable ExecutionEnvironment environment, final @Nullable ProgramRunner runner) { + ExecutionResult executionResult = createExecutionResult(session); + myConsole = executionResult.getExecutionConsole(); + myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName()); + + myUi.addContent(createFramesContent(session), 0, PlaceInGrid.left, false); + myUi.addContent(createVariablesContent(session), 0, PlaceInGrid.center, false); + myUi.addContent(createWatchesContent(session, sessionData), 0, PlaceInGrid.right, false); + final Content consoleContent = createConsoleContent(); + myUi.addContent(consoleContent, 1, PlaceInGrid.bottom, false); + if (myConsole instanceof ObservableConsoleView) { + ObservableConsoleView observable = (ObservableConsoleView)myConsole; + observable.addChangeListener(new ObservableConsoleView.ChangeListener() { + public void contentAdded(final Collection types) { + if (types.contains(ConsoleViewContentType.ERROR_OUTPUT) || types.contains(ConsoleViewContentType.SYSTEM_OUTPUT)) { + consoleContent.fireAlert(); + } + } + }, consoleContent); + } + session.getDebugProcess().registerAdditionalContent(myUi); + RunContentBuilder.addAdditionalConsoleEditorActions(myConsole, consoleContent); + myUi.addContent(consoleContent, 0, PlaceInGrid.bottom, false); + + if (ApplicationManager.getApplication().isUnitTestMode()) { + return myRunContentDescriptor; + } + + DefaultActionGroup group = new DefaultActionGroup(); + final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); + if (runner != null && environment != null) { + RestartAction restartAction = new RestartAction(executor, runner, myRunContentDescriptor.getProcessHandler(), XDebuggerUIConstants.DEBUG_AGAIN_ICON, + myRunContentDescriptor, environment); + group.add(restartAction); + restartAction.registerShortcut(myUi.getComponent()); + } + + addActionToGroup(group, XDebuggerActions.RESUME); + addActionToGroup(group, XDebuggerActions.PAUSE); + addActionToGroup(group, IdeActions.ACTION_STOP_PROGRAM); + + group.addSeparator(); + + addActionToGroup(group, XDebuggerActions.VIEW_BREAKPOINTS); + addActionToGroup(group, XDebuggerActions.MUTE_BREAKPOINTS); + + group.addSeparator(); + //addAction(group, DebuggerActions.EXPORT_THREADS); + group.addSeparator(); + + group.add(myUi.getOptions().getLayoutActions()); + + group.addSeparator(); + + group.add(PinToolwindowTabAction.getPinAction()); + group.add(new CloseAction(executor, myRunContentDescriptor, getProject())); + group.add(new ContextHelpAction(executor.getHelpId())); + + myUi.getOptions().setLeftToolbar(group, ActionPlaces.DEBUGGER_TOOLBAR); + + if (environment != null) { + final RunProfile runConfiguration = environment.getRunProfile(); + registerFileMatcher(runConfiguration); + initLogConsoles(runConfiguration, myRunContentDescriptor.getProcessHandler()); + } + + rebuildViews(); + + return myRunContentDescriptor; + } + + private static void addActionToGroup(final DefaultActionGroup group, final String actionId) { + AnAction action = ActionManager.getInstance().getAction(actionId); + if (action != null) group.add(action); + } + + public RunnerLayoutUi getUi() { + return myUi; + } + + @Nullable + public RunContentDescriptor getRunContentDescriptor() { + return myRunContentDescriptor; + } } \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/SetValueInplaceEditor.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/SetValueInplaceEditor.java index 0dd937abc9c2..f2950c03aef6 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/SetValueInplaceEditor.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/SetValueInplaceEditor.java @@ -1,85 +1,85 @@ -/* - * Copyright 2000-2009 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.xdebugger.impl.ui.tree; - -import com.intellij.codeInsight.hint.HintManager; -import com.intellij.ui.SimpleColoredComponent; -import com.intellij.xdebugger.frame.XValueModifier; -import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; -import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; -import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; - -/** - * @author nik - */ -public class SetValueInplaceEditor extends XDebuggerTreeInplaceEditor { - private final JPanel myEditorPanel; - private final XValueModifier myModifier; - private final XValueNodeImpl myValueNode; - - public SetValueInplaceEditor(final XValueNodeImpl node, @NotNull final String nodeName) { - super(node, "setValue"); - myValueNode = node; - myModifier = myValueNode.getValueContainer().getModifier(); - - myEditorPanel = new JPanel(); - myEditorPanel.setLayout(new BoxLayout(myEditorPanel, BoxLayout.X_AXIS)); - SimpleColoredComponent nameLabel = new SimpleColoredComponent(); - nameLabel.setIcon(getNode().getIcon()); - nameLabel.append(nodeName, XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES); - - myEditorPanel.add(nameLabel); - - myEditorPanel.add(myExpressionEditor.getComponent()); - final String value = myModifier != null ? myModifier.getInitialValueEditorText() : null; - myExpressionEditor.setText(value != null ? value : ""); - myExpressionEditor.selectAll(); - } - - protected JComponent createInplaceEditorComponent() { - return myEditorPanel; - } - - public void doOKAction() { - if (myModifier == null) return; - - myExpressionEditor.saveTextInHistory(); - final XDebuggerTreeState treeState = XDebuggerTreeState.saveState(myTree); - myValueNode.setValueModificationStarted(); - myModifier.setValue(myExpressionEditor.getText(), new XValueModifier.XModificationCallback() { - public void valueModified() { - DebuggerUIUtil.invokeOnEventDispatch(new Runnable() { - public void run() { - myTree.rebuildAndRestore(treeState); - } - }); - } - - public void errorOccurred(@NotNull final String errorMessage) { - DebuggerUIUtil.invokeOnEventDispatch(new Runnable() { - public void run() { - myTree.rebuildAndRestore(treeState); - HintManager.getInstance().showErrorHint(myExpressionEditor.getEditor(), errorMessage); - } - }); - } - }); - super.doOKAction(); - } -} +/* + * Copyright 2000-2009 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.xdebugger.impl.ui.tree; + +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.xdebugger.frame.XValueModifier; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; +import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author nik + */ +public class SetValueInplaceEditor extends XDebuggerTreeInplaceEditor { + private final JPanel myEditorPanel; + private final XValueModifier myModifier; + private final XValueNodeImpl myValueNode; + + public SetValueInplaceEditor(final XValueNodeImpl node, @NotNull final String nodeName) { + super(node, "setValue"); + myValueNode = node; + myModifier = myValueNode.getValueContainer().getModifier(); + + myEditorPanel = new JPanel(); + myEditorPanel.setLayout(new BoxLayout(myEditorPanel, BoxLayout.X_AXIS)); + SimpleColoredComponent nameLabel = new SimpleColoredComponent(); + nameLabel.setIcon(getNode().getIcon()); + nameLabel.append(nodeName, XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES); + + myEditorPanel.add(nameLabel); + + myEditorPanel.add(myExpressionEditor.getComponent()); + final String value = myModifier != null ? myModifier.getInitialValueEditorText() : null; + myExpressionEditor.setText(value != null ? value : ""); + myExpressionEditor.selectAll(); + } + + protected JComponent createInplaceEditorComponent() { + return myEditorPanel; + } + + public void doOKAction() { + if (myModifier == null) return; + + myExpressionEditor.saveTextInHistory(); + final XDebuggerTreeState treeState = XDebuggerTreeState.saveState(myTree); + myValueNode.setValueModificationStarted(); + myModifier.setValue(myExpressionEditor.getText(), new XValueModifier.XModificationCallback() { + public void valueModified() { + DebuggerUIUtil.invokeOnEventDispatch(new Runnable() { + public void run() { + myTree.rebuildAndRestore(treeState); + } + }); + } + + public void errorOccurred(@NotNull final String errorMessage) { + DebuggerUIUtil.invokeOnEventDispatch(new Runnable() { + public void run() { + myTree.rebuildAndRestore(treeState); + HintManager.getInstance().showErrorHint(myExpressionEditor.getEditor(), errorMessage); + } + }); + } + }); + super.doOKAction(); + } +}