| /* |
| * Copyright 2000-2013 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.jetbrains.python.console; |
| |
| import com.intellij.codeInsight.hint.HintManager; |
| import com.intellij.execution.console.LanguageConsoleImpl; |
| import com.intellij.execution.console.LanguageConsoleView; |
| import com.intellij.execution.console.LanguageConsoleViewImpl; |
| import com.intellij.execution.filters.Filter; |
| import com.intellij.execution.filters.HyperlinkInfo; |
| import com.intellij.execution.filters.OpenFileHyperlinkInfo; |
| import com.intellij.execution.process.ProcessHandler; |
| import com.intellij.execution.process.ProcessOutputTypes; |
| import com.intellij.execution.ui.ConsoleViewContentType; |
| import com.intellij.execution.ui.ObservableConsoleView; |
| import com.intellij.ide.GeneralSettings; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.colors.EditorColorsScheme; |
| import com.intellij.openapi.fileEditor.FileDocumentManager; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.progress.Task; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.wm.IdeFocusManager; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.xdebugger.impl.frame.XStandaloneVariablesView; |
| import com.jetbrains.python.PythonLanguage; |
| import com.jetbrains.python.console.completion.PythonConsoleAutopopupBlockingHandler; |
| import com.jetbrains.python.console.pydev.ConsoleCommunication; |
| import com.jetbrains.python.console.pydev.ConsoleCommunicationListener; |
| import com.jetbrains.python.debugger.PyDebuggerEditorsProvider; |
| import com.jetbrains.python.debugger.PyStackFrame; |
| import com.jetbrains.python.debugger.PyStackFrameInfo; |
| import com.jetbrains.python.highlighting.PyHighlighter; |
| import com.jetbrains.python.psi.LanguageLevel; |
| import com.jetbrains.python.sdk.PythonSdkType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.FocusListener; |
| |
| /** |
| * @author traff |
| */ |
| public class PythonConsoleView extends JPanel implements LanguageConsoleView, ObservableConsoleView, PyCodeExecutor { |
| |
| private static final Logger LOG = Logger.getInstance(PythonConsoleView.class); |
| |
| private final Project myProject; |
| private PydevConsoleExecuteActionHandler myExecuteActionHandler; |
| private PyConsoleSourceHighlighter mySourceHighlighter; |
| private boolean myIsIPythonOutput = false; |
| private final PyHighlighter myPyHighlighter; |
| private final EditorColorsScheme myScheme; |
| private boolean myHyperlink; |
| |
| private final LanguageConsoleViewImpl myLanguageConsoleView; |
| |
| private Disposable mySplitDisposable; |
| |
| public PythonConsoleView(final Project project, final String title, final Sdk sdk) { |
| super(new BorderLayout()); |
| |
| LanguageConsoleImpl languageConsole = new LanguageConsoleImpl(project, title, PythonLanguage.getInstance(), false); |
| if (languageConsole.getFile().getVirtualFile() != null) { |
| languageConsole.getFile().getVirtualFile().putUserData(LanguageLevel.KEY, PythonSdkType.getLanguageLevelForSdk(sdk)); |
| } |
| // Mark editor as console one, to prevent autopopup completion |
| languageConsole.getConsoleEditor().putUserData(PythonConsoleAutopopupBlockingHandler.REPL_KEY, new Object()); |
| languageConsole.setShowSeparatorLine(PyConsoleOptions.getInstance(project).isShowSeparatorLine()); |
| languageConsole.initComponents(); |
| |
| myLanguageConsoleView = new LanguageConsoleViewImpl(languageConsole); |
| Disposer.register(this, myLanguageConsoleView); |
| |
| add(myLanguageConsoleView.getComponent(), BorderLayout.CENTER); |
| |
| addSaveContentFocusListener(getLanguageConsole().getConsoleEditor().getContentComponent()); |
| |
| getPythonLanguageConsole().setPrompt(PyConsoleUtil.ORDINARY_PROMPT); |
| myLanguageConsoleView.setUpdateFoldingsEnabled(false); |
| myProject = project; |
| //noinspection ConstantConditions |
| myPyHighlighter = new PyHighlighter( |
| sdk != null && sdk.getVersionString() != null ? LanguageLevel.fromPythonVersion(sdk.getVersionString()) : LanguageLevel.getDefault()); |
| myScheme = getPythonLanguageConsole().getConsoleEditor().getColorsScheme(); |
| } |
| |
| public void setConsoleCommunication(final ConsoleCommunication communication) { |
| getPythonLanguageConsole().getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_KEY, communication); |
| } |
| |
| public void setExecutionHandler(@NotNull PydevConsoleExecuteActionHandler consoleExecuteActionHandler) { |
| myExecuteActionHandler = consoleExecuteActionHandler; |
| } |
| |
| private void addSaveContentFocusListener(JComponent component) { |
| component.addFocusListener(new FocusListener() { |
| @Override |
| public void focusGained(FocusEvent e) { |
| if (GeneralSettings.getInstance().isSaveOnFrameDeactivation()) { |
| FileDocumentManager.getInstance().saveAllDocuments(); |
| } |
| } |
| |
| @Override |
| public void focusLost(FocusEvent e) { |
| } |
| }); |
| } |
| |
| @Override |
| public void requestFocus() { |
| IdeFocusManager.findInstance().requestFocus(getPythonLanguageConsole().getConsoleEditor().getContentComponent(), true); |
| myLanguageConsoleView.updateUI(); |
| getLanguageConsole().getHistoryViewer().getComponent().updateUI(); |
| } |
| |
| private LanguageConsoleImpl getPythonLanguageConsole() { |
| return getLanguageConsole(); |
| } |
| |
| public LanguageConsoleImpl getLanguageConsole() { |
| return myLanguageConsoleView.getConsole(); |
| } |
| |
| @Override |
| public void executeCode(final @NotNull String code, @Nullable final Editor editor) { |
| showConsole(new Runnable() { |
| @Override |
| public void run() { |
| ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing code in console...", false) { |
| @Override |
| public void run(@NotNull final ProgressIndicator indicator) { |
| long time = System.currentTimeMillis(); |
| while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) { |
| if (indicator.isCanceled()) { |
| break; |
| } |
| if (System.currentTimeMillis() - time > 1000) { |
| if (editor != null) { |
| UIUtil.invokeLaterIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage()); |
| } |
| }); |
| } |
| return; |
| } |
| try { |
| Thread.sleep(300); |
| } |
| catch (InterruptedException ignored) { |
| } |
| } |
| if (!indicator.isCanceled()) { |
| doExecute(code); |
| } |
| } |
| }); |
| } |
| }); |
| } |
| |
| private void showConsole(@NotNull Runnable runnable) { |
| PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject); |
| if (toolWindow != null && !ApplicationManager.getApplication().isUnitTestMode()) { |
| toolWindow.getToolWindow().activate(runnable); |
| } |
| else { |
| runnable.run(); |
| } |
| } |
| |
| |
| private void doExecute(String code) { |
| String codeFragment = PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize()); |
| codeFragment += "\n"; |
| executeInConsole(codeFragment); |
| } |
| |
| public void executeInConsole(final String code) { |
| UIUtil.invokeLaterIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| String text = getPythonLanguageConsole().getConsoleEditor().getDocument().getText(); |
| |
| getPythonLanguageConsole().setInputText(code); |
| myExecuteActionHandler.runExecuteAction(myLanguageConsoleView); |
| |
| if (!StringUtil.isEmpty(text)) { |
| getPythonLanguageConsole().setInputText(text); |
| } |
| } |
| }); |
| } |
| |
| public void executeStatement(@NotNull String statement, @NotNull final Key attributes) { |
| print(statement, outputTypeForAttributes(attributes)); |
| myExecuteActionHandler.processLine(statement, true); |
| } |
| |
| public void print(String text, @NotNull final Key attributes) { |
| print(text, outputTypeForAttributes(attributes)); |
| } |
| |
| public void printText(String text, final ConsoleViewContentType outputType) { |
| myLanguageConsoleView.print(text, outputType); |
| } |
| |
| @Override |
| public void print(@NotNull String text, @NotNull final ConsoleViewContentType outputType) { |
| detectIPython(text, outputType); |
| if (PyConsoleUtil.detectIPythonEnd(text)) { |
| myIsIPythonOutput = false; |
| mySourceHighlighter = null; |
| } |
| else if (PyConsoleUtil.detectIPythonStart(text)) { |
| myIsIPythonOutput = true; |
| } |
| else { |
| if (mySourceHighlighter == null || outputType == ConsoleViewContentType.ERROR_OUTPUT) { |
| if (myHyperlink) { |
| printHyperlink(text, outputType); |
| } |
| else { |
| //Print text normally with converted attributes |
| myLanguageConsoleView.print(text, outputType); |
| } |
| myHyperlink = detectHyperlink(text); |
| if (mySourceHighlighter == null && myIsIPythonOutput && PyConsoleUtil.detectSourcePrinting(text)) { |
| mySourceHighlighter = new PyConsoleSourceHighlighter(this, myScheme, myPyHighlighter); |
| } |
| } |
| else { |
| try { |
| mySourceHighlighter.printHighlightedSource(text); |
| } |
| catch (Exception e) { |
| LOG.error(e); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void clear() { |
| myLanguageConsoleView.clear(); |
| } |
| |
| @Override |
| public void scrollTo(int offset) { |
| myLanguageConsoleView.scrollTo(offset); |
| } |
| |
| @Override |
| public void attachToProcess(ProcessHandler processHandler) { |
| myLanguageConsoleView.attachToProcess(processHandler); |
| } |
| |
| @Override |
| public void setOutputPaused(boolean value) { |
| myLanguageConsoleView.setOutputPaused(value); |
| } |
| |
| @Override |
| public boolean isOutputPaused() { |
| return myLanguageConsoleView.isOutputPaused(); |
| } |
| |
| @Override |
| public boolean hasDeferredOutput() { |
| return myLanguageConsoleView.hasDeferredOutput(); |
| } |
| |
| @Override |
| public void performWhenNoDeferredOutput(Runnable runnable) { |
| myLanguageConsoleView.performWhenNoDeferredOutput(runnable); |
| } |
| |
| @Override |
| public void setHelpId(String helpId) { |
| myLanguageConsoleView.setHelpId(helpId); |
| } |
| |
| @Override |
| public void addMessageFilter(Filter filter) { |
| myLanguageConsoleView.addMessageFilter(filter); |
| } |
| |
| @Override |
| public void printHyperlink(String hyperlinkText, HyperlinkInfo info) { |
| myLanguageConsoleView.printHyperlink(hyperlinkText, info); |
| } |
| |
| @Override |
| public int getContentSize() { |
| return myLanguageConsoleView.getContentSize(); |
| } |
| |
| @Override |
| public boolean canPause() { |
| return myLanguageConsoleView.canPause(); |
| } |
| |
| @NotNull |
| @Override |
| public AnAction[] createConsoleActions() { |
| return myLanguageConsoleView.createConsoleActions(); |
| } |
| |
| @Override |
| public void allowHeavyFilters() { |
| myLanguageConsoleView.allowHeavyFilters(); |
| } |
| |
| public void detectIPython(String text, final ConsoleViewContentType outputType) { |
| VirtualFile file = getConsoleVirtualFile(); |
| if (file != null) { |
| if (PyConsoleUtil.detectIPythonImported(text, outputType)) { |
| PyConsoleUtil.markIPython(file); |
| } |
| if (PyConsoleUtil.detectIPythonAutomagicOn(text)) { |
| PyConsoleUtil.setIPythonAutomagic(file, true); |
| } |
| if (PyConsoleUtil.detectIPythonAutomagicOff(text)) { |
| PyConsoleUtil.setIPythonAutomagic(file, false); |
| } |
| } |
| } |
| |
| public VirtualFile getConsoleVirtualFile() { |
| return getLanguageConsole().getFile().getVirtualFile(); |
| } |
| |
| private boolean detectHyperlink(@NotNull String text) { |
| return myIsIPythonOutput && text.startsWith("File:"); |
| } |
| |
| private void printHyperlink(@NotNull String text, @NotNull ConsoleViewContentType contentType) { |
| if (!StringUtil.isEmpty(text)) { |
| VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(text.trim()); |
| |
| if (vFile != null) { |
| OpenFileHyperlinkInfo hyperlink = new OpenFileHyperlinkInfo(myProject, vFile, -1); |
| |
| myLanguageConsoleView.printHyperlink(text, hyperlink); |
| } |
| else { |
| myLanguageConsoleView.print(text, contentType); |
| } |
| } |
| } |
| |
| public ConsoleViewContentType outputTypeForAttributes(Key attributes) { |
| final ConsoleViewContentType outputType; |
| if (attributes == ProcessOutputTypes.STDERR) { |
| outputType = ConsoleViewContentType.ERROR_OUTPUT; |
| } |
| else if (attributes == ProcessOutputTypes.SYSTEM) { |
| outputType = ConsoleViewContentType.SYSTEM_OUTPUT; |
| } |
| else { |
| outputType = ConsoleViewContentType.getConsoleViewType(attributes); |
| } |
| |
| return outputType; |
| } |
| |
| public void setSdk(Sdk sdk) { |
| getPythonLanguageConsole().getFile().putCopyableUserData(PydevConsoleRunner.CONSOLE_SDK, sdk); |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return this; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusableComponent() { |
| return myLanguageConsoleView.getPreferredFocusableComponent(); |
| } |
| |
| @Override |
| public void dispose() { |
| Disposer.dispose(this); |
| } |
| |
| @Override |
| public void addChangeListener(@NotNull ChangeListener listener, @NotNull Disposable parent) { |
| myLanguageConsoleView.addChangeListener(listener, parent); |
| } |
| |
| @NotNull |
| @Override |
| public LanguageConsoleImpl getConsole() { |
| return myLanguageConsoleView.getConsole(); |
| } |
| |
| @NotNull |
| @Override |
| public Project getProject() { |
| return myProject; |
| } |
| |
| public void showVariables(PydevConsoleCommunication consoleCommunication) { |
| PyStackFrame stackFrame = new PyStackFrame(myProject, consoleCommunication, new PyStackFrameInfo("", "", "", null), null); |
| final XStandaloneVariablesView view = new XStandaloneVariablesView(myProject, new PyDebuggerEditorsProvider(), stackFrame); |
| consoleCommunication.addCommunicationListener(new ConsoleCommunicationListener() { |
| @Override |
| public void commandExecuted(boolean more) { |
| view.rebuildView(); |
| } |
| |
| @Override |
| public void inputRequested() { |
| } |
| }); |
| splitWindow(view.getPanel(), view); |
| } |
| |
| private void splitWindow(JComponent component, Disposable componentDisposable) { |
| removeAll(); |
| JSplitPane p = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); |
| p.add(myLanguageConsoleView.getComponent(), JSplitPane.LEFT); |
| mySplitDisposable = componentDisposable; |
| p.add(component, JSplitPane.RIGHT); |
| p.setDividerLocation((int)getSize().getWidth() * 2 / 3); |
| add(p, BorderLayout.CENTER); |
| |
| validate(); |
| repaint(); |
| } |
| |
| public void restoreWindow() { |
| removeAll(); |
| add(myLanguageConsoleView.getComponent(), BorderLayout.CENTER); |
| validate(); |
| repaint(); |
| if (mySplitDisposable != null) { |
| Disposer.dispose(mySplitDisposable); |
| mySplitDisposable = null; |
| } |
| } |
| } |