/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.debug.console; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.internal.ui.views.console.ProcessConsole; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsolePageParticipant; import org.eclipse.ui.console.TextConsoleViewer; import org.eclipse.ui.internal.console.IOConsolePage; import org.eclipse.ui.internal.console.IOConsolePartition; import org.eclipse.ui.part.IPageBookViewPage; import org.python.pydev.core.IInterpreterInfo; import org.python.pydev.core.log.Log; import org.python.pydev.debug.core.Constants; import org.python.pydev.debug.model.AbstractDebugTarget; import org.python.pydev.debug.model.PyDebugTarget; import org.python.pydev.debug.model.PyStackFrame; import org.python.pydev.debug.model.XMLUtils; import org.python.pydev.debug.model.remote.AbstractDebuggerCommand; import org.python.pydev.debug.model.remote.GetCompletionsCommand; import org.python.pydev.debug.model.remote.ICommandResponseListener; import org.python.pydev.debug.newconsole.PydevConsoleCommunication; import org.python.pydev.debug.newconsole.PydevConsoleCompletionProcessor; import org.python.pydev.debug.newconsole.PydevConsoleInterpreter; import org.python.pydev.editor.codecompletion.PyCodeCompletionPreferencesPage; import org.python.pydev.editor.codecompletion.PyContentAssistant; import org.python.pydev.plugin.nature.PythonNature; import com.aptana.interactive_console.console.IScriptConsoleCommunication; import com.aptana.interactive_console.console.InterpreterResponse; import com.aptana.shared_core.bindings.KeyBindingHelper; import com.aptana.shared_core.callbacks.ICallback; import com.aptana.shared_core.structure.Tuple; /** * Will provide code-completion in debug sessions. */ @SuppressWarnings("restriction") public class ConsoleCompletionsPageParticipant implements IConsolePageParticipant { /** * @return the currently selected / suspended frame. If the console is passed, it will only return * a frame that matches the passed console. If no selected / suspended frame is found or the console * doesn't match, null is returned. */ protected static PyStackFrame getCurrentSuspendedPyStackFrame(IConsole console) { IAdaptable context = DebugUITools.getDebugContext(); if (context instanceof PyStackFrame) { PyStackFrame stackFrame = (PyStackFrame) context; if (!stackFrame.isTerminated() && stackFrame.isSuspended()) { if (console != null) { //If a console is passed, we must check if it matches the console from the selected frame. AbstractDebugTarget target = (AbstractDebugTarget) stackFrame.getAdapter(IDebugTarget.class); if (DebugUITools.getConsole(target.getProcess()) != console) { return null; } } return stackFrame; } } return null; } /** * Class to get the completions in debug mode in a suspended frame. */ public static class GetCompletionsInDebug implements IScriptConsoleCommunication, ICommandResponseListener { private static final ICompletionProposal[] EMPTY_COMPLETION_PROPOSALS = new ICompletionProposal[0]; private ICompletionProposal[] receivedCompletions; private String actTok; private String text; private int offset; public String getDescription(String text) throws Exception { throw new RuntimeException("Not implemented"); } /** * Gets the completions at the passed offset. */ public ICompletionProposal[] getCompletions(String text, String actTok, int offset) throws Exception { this.text = text; this.actTok = actTok; this.offset = offset; PyStackFrame stackFrame = getCurrentSuspendedPyStackFrame(null); if (stackFrame != null) { AbstractDebugTarget target = (AbstractDebugTarget) stackFrame.getAdapter(IDebugTarget.class); if (target != null) { GetCompletionsCommand cmd = new GetCompletionsCommand(target, actTok, stackFrame.getLocalsLocator() .getPyDBLocation()); cmd.setCompletionListener(this); target.postCommand(cmd); } return waitForCommand(); } return EMPTY_COMPLETION_PROPOSALS; } /** * Keeps in a loop for 3 seconds or until the completions are found. If no completions are found in that time, * returns an empty array. */ private ICompletionProposal[] waitForCommand() { int i = 300; //wait up to 3 seconds while (--i > 0 && receivedCompletions == null) { try { Thread.sleep(10); //10 millis } catch (InterruptedException e) { //ignore } } ICompletionProposal[] temp = receivedCompletions; receivedCompletions = null; if (temp == null) { Log.logInfo("Timeout for waiting for debug completions elapsed (3 seconds)."); return EMPTY_COMPLETION_PROPOSALS; } return temp; } public void execInterpreter(String command, ICallback<Object, InterpreterResponse> onResponseReceived, ICallback<Object, Tuple<String, String>> onContentsReceived) { throw new RuntimeException("Not implemented"); } public void close() throws Exception { throw new RuntimeException("Not implemented"); } /** * Received when the completions command receives a response (ICommandResponseListener) * Converts the xml to completions. */ public void commandComplete(AbstractDebuggerCommand cmd) { GetCompletionsCommand compCmd = (GetCompletionsCommand) cmd; try { String response = compCmd.getResponse(); List<Object[]> fromServer = XMLUtils.convertXMLcompletionsFromConsole(response); List<ICompletionProposal> ret = new ArrayList<ICompletionProposal>(); PydevConsoleCommunication.convertToICompletions(text, actTok, offset, fromServer, ret); receivedCompletions = ret.toArray(new ICompletionProposal[ret.size()]); } catch (CoreException e) { receivedCompletions = EMPTY_COMPLETION_PROPOSALS; Log.log(e); } } public void linkWithDebugSelection(boolean isLinkedWithDebug) { throw new RuntimeException("Not implemented"); } } /** * The content assistant added to the console. */ private PyContentAssistant contentAssist; public Object getAdapter(Class adapter) { return null; } /** * When a console page is initialized, */ public void init(IPageBookViewPage page, final IConsole console) { if (!(console instanceof ProcessConsole)) { return; } ProcessConsole processConsole = (ProcessConsole) console; IProcess process = processConsole.getProcess(); if (process == null) { return; } if (!PyCodeCompletionPreferencesPage.useCodeCompletion() || !PyCodeCompletionPreferencesPage.useCodeCompletionOnDebug()) { return; } String attribute = process.getAttribute(Constants.PYDEV_DEBUG_IPROCESS_ATTR); if (!Constants.PYDEV_DEBUG_IPROCESS_ATTR_TRUE.equals(attribute)) { //Only provide code-completion for pydev debug processes. return; } Control control = page.getControl(); if (page instanceof IOConsolePage) { //Note that completions on "all letters and '_'" are already activated just by installing //the content assist, but the completions on the default keybinding is not, so, we have to //call it ourselves here. control.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { if (KeyBindingHelper.matchesContentAssistKeybinding(e)) { contentAssist.showPossibleCompletions(); } } public void keyReleased(KeyEvent e) { } }); IOConsolePage consolePage = (IOConsolePage) page; TextConsoleViewer viewer = consolePage.getViewer(); contentAssist = new PyContentAssistant() { public String showPossibleCompletions() { //Only show completions if we're in a suspended console. if (getCurrentSuspendedPyStackFrame(console) == null) { return null; } return super.showPossibleCompletions(); }; }; contentAssist.setInformationControlCreator(PyContentAssistant.createInformationControlCreator(viewer)); ILaunch launch = process.getLaunch(); IDebugTarget debugTarget = launch.getDebugTarget(); IInterpreterInfo projectInterpreter = null; if (debugTarget instanceof PyDebugTarget) { PyDebugTarget pyDebugTarget = (PyDebugTarget) debugTarget; PythonNature nature = PythonNature.getPythonNature(pyDebugTarget.project); if (nature != null) { try { projectInterpreter = nature.getProjectInterpreter(); } catch (Throwable e1) { Log.log(e1); } } } contentAssist.install(new ScriptConsoleViewerWrapper(viewer, projectInterpreter)); PydevConsoleInterpreter interpreter = new PydevConsoleInterpreter(); interpreter.setConsoleCommunication(new GetCompletionsInDebug()); IContentAssistProcessor processor = new PydevConsoleCompletionProcessor(interpreter, contentAssist); contentAssist.setContentAssistProcessor(processor, IOConsolePartition.INPUT_PARTITION_TYPE); contentAssist.setContentAssistProcessor(processor, IOConsolePartition.OUTPUT_PARTITION_TYPE); contentAssist.enableAutoActivation(true); contentAssist.enableAutoInsert(false); contentAssist.setAutoActivationDelay(PyCodeCompletionPreferencesPage.getAutocompleteDelay()); } } public void dispose() { } public void activated() { } public void deactivated() { } }