add support for parameter info in console

This commit is contained in:
fitermay
2016-08-28 20:59:08 -04:00
parent 770f5f52eb
commit 4484907fe9
7 changed files with 406 additions and 203 deletions
@@ -0,0 +1,160 @@
'''
Id: "$Id$"
Copyright: Copyright (c) 2015 Bank of America Merrill Lynch, All Rights Reserved
Description:
Test:
'''
# noinspection PyBroadException
import types
from _pydevd_bundle.pydevd_constants import IS_JYTHON, IS_PY3K
try:
import inspect
except:
try:
from _pydev_imps import _pydev_inspect as inspect
except:
import traceback;
traceback.print_exc() # Ok, no inspect available (search will not work)from _pydevd_bundle.pydevd_constants import IS_JYTHON, IS_PY3K
from _pydev_bundle._pydev_imports_tipper import signature_from_docstring
def is_bound_method(obj):
if isinstance(obj, types.MethodType):
return getattr(obj, '__self__', getattr(obj, 'im_self', None)) is not None
else:
return False
def get_class_name(instance):
return getattr(getattr(instance, "__class__", None), "__name__", None)
def get_bound_class_name(obj):
my_self = getattr(obj, '__self__', getattr(obj, 'im_self', None))
if my_self is None:
return None
return get_class_name(my_self)
def get_description(obj):
try:
ob_call = obj.__call__
except:
ob_call = None
if isinstance(obj, type) or type(obj).__name__ == 'classobj':
fob = getattr(obj, '__init__', lambda: None)
if not isinstance(fob, (types.FunctionType, types.MethodType)):
fob = obj
elif is_bound_method(ob_call):
fob = ob_call
else:
fob = obj
argspec = ""
fn_name = None
fn_class = None
if isinstance(fob, (types.FunctionType, types.MethodType)):
spec_info = inspect.getfullargspec(fob) if IS_PY3K else inspect.getargspec(fob)
argspec = inspect.formatargspec(*spec_info)
fn_name = getattr(fob, '__name__', None)
if isinstance(obj, type) or type(obj).__name__ == 'classobj':
fn_name = "__init__"
fn_class = getattr(obj, "__name__", "UnknownClass")
elif is_bound_method(obj) or is_bound_method(ob_call):
fn_class = get_bound_class_name(obj) or "UnknownClass"
else:
fn_name = getattr(fob, '__name__', None)
fn_self = getattr(fob, '__self__', None)
if fn_self is not None and not isinstance(fn_self, types.ModuleType):
fn_class = get_class_name(fn_self)
doc_string = get_docstring(ob_call) if is_bound_method(ob_call) else get_docstring(obj)
return create_method_stub(fn_name, fn_class, argspec, doc_string)
def create_method_stub(fn_name, fn_class, argspec, doc_string):
if fn_name and argspec:
doc_string = "" if doc_string is None else doc_string
fn_stub = create_function_stub(fn_name, argspec, doc_string, indent=1 if fn_class else 0)
if fn_class:
expr = fn_class if fn_name == '__init__' else fn_class + '().' + fn_name
return create_class_stub(fn_class, fn_stub) + "\n" + expr
else:
expr = fn_name
return fn_stub + "\n" + expr
elif doc_string:
if fn_name:
restored_signature, _ = signature_from_docstring(doc_string, fn_name)
if restored_signature:
return create_method_stub(fn_name, fn_class, restored_signature, doc_string)
return create_function_stub('unknown', '(*args, **kwargs)', doc_string) + '\nunknown'
else:
return ''
def get_docstring(obj):
if obj is not None:
try:
if IS_JYTHON:
# Jython
doc = obj.__doc__
if doc is not None:
return doc
from _pydev_bundle import _pydev_jy_imports_tipper
is_method, infos = _pydev_jy_imports_tipper.ismethod(obj)
ret = ''
if is_method:
for info in infos:
ret += info.get_as_doc()
return ret
else:
doc = inspect.getdoc(obj)
if doc is not None:
return doc
except:
pass
else:
return ''
try:
# if no attempt succeeded, try to return repr()...
return repr(obj)
except:
try:
# otherwise the class
return str(obj.__class__)
except:
# if all fails, go to an empty string
return ''
def create_class_stub(class_name, contents):
return "class %s(object):\n%s" % (class_name, contents)
def create_function_stub(fn_name, fn_argspec, fn_docstring, indent=0):
def shift_right(string, prefix):
return ''.join(prefix + line for line in string.splitlines(True))
fn_docstring = shift_right(inspect.cleandoc(fn_docstring), " " * (indent + 1))
ret = '''
def %s%s:
"""%s"""
pass
''' % (fn_name, fn_argspec, fn_docstring)
ret = ret[1:] # remove first /n
ret = ret.replace('\t', " ")
if indent:
prefix = " " * indent
ret = shift_right(ret, prefix)
return ret
@@ -1,5 +1,5 @@
import os.path
import inspect
import os.path
import sys
from _pydev_bundle._pydev_tipper_common import do_find
@@ -222,96 +222,7 @@ def generate_imports_tip_for_module(obj_to_complete, dirComps=None, getattr=geta
args = '(%s)' % (r)
except TypeError:
#ok, let's see if we can get the arguments from the doc
args = '()'
try:
found = False
if len(doc) > 0:
if IS_IPY:
#Handle case where we have the situation below
#sort(self, object cmp, object key)
#sort(self, object cmp, object key, bool reverse)
#sort(self)
#sort(self, object cmp)
#Or: sort(self: list, cmp: object, key: object)
#sort(self: list, cmp: object, key: object, reverse: bool)
#sort(self: list)
#sort(self: list, cmp: object)
if hasattr(obj, '__name__'):
name = obj.__name__+'('
#Fix issue where it was appearing sort(aa)sort(bb)sort(cc) in the same line.
lines = doc.splitlines()
if len(lines) == 1:
c = doc.count(name)
if c > 1:
doc = ('\n'+name).join(doc.split(name))
major = ''
for line in doc.splitlines():
if line.startswith(name) and line.endswith(')'):
if len(line) > len(major):
major = line
if major:
args = major[major.index('('):]
found = True
if not found:
i = doc.find('->')
if i < 0:
i = doc.find('--')
if i < 0:
i = doc.find('\n')
if i < 0:
i = doc.find('\r')
if i > 0:
s = doc[0:i]
s = s.strip()
#let's see if we have a docstring in the first line
if s[-1] == ')':
start = s.find('(')
if start >= 0:
end = s.find('[')
if end <= 0:
end = s.find(')')
if end <= 0:
end = len(s)
args = s[start:end]
if not args[-1] == ')':
args = args + ')'
#now, get rid of unwanted chars
l = len(args) - 1
r = []
for i in xrange(len(args)):
if i == 0 or i == l:
r.append(args[i])
else:
r.append(check_char(args[i]))
args = ''.join(r)
if IS_IPY:
if args.startswith('(self:'):
i = args.find(',')
if i >= 0:
args = '(self'+args[i:]
else:
args = '(self)'
i = args.find(')')
if i > 0:
args = args[:i+1]
except:
pass
args, doc = signature_from_docstring(doc, getattr(obj, '__name__', None))
retType = TYPE_FUNCTION
@@ -350,5 +261,90 @@ def generate_imports_tip_for_module(obj_to_complete, dirComps=None, getattr=geta
return ret
def signature_from_docstring(doc, obj_name):
args = '()'
try:
found = False
if len(doc) > 0:
if IS_IPY:
# Handle case where we have the situation below
# sort(self, object cmp, object key)
# sort(self, object cmp, object key, bool reverse)
# sort(self)
# sort(self, object cmp)
# Or: sort(self: list, cmp: object, key: object)
# sort(self: list, cmp: object, key: object, reverse: bool)
# sort(self: list)
# sort(self: list, cmp: object)
if obj_name:
name = obj_name + '('
# Fix issue where it was appearing sort(aa)sort(bb)sort(cc) in the same line.
lines = doc.splitlines()
if len(lines) == 1:
c = doc.count(name)
if c > 1:
doc = ('\n' + name).join(doc.split(name))
major = ''
for line in doc.splitlines():
if line.startswith(name) and line.endswith(')'):
if len(line) > len(major):
major = line
if major:
args = major[major.index('('):]
found = True
if not found:
i = doc.find('->')
if i < 0:
i = doc.find('--')
if i < 0:
i = doc.find('\n')
if i < 0:
i = doc.find('\r')
if i > 0:
s = doc[0:i]
s = s.strip()
# let's see if we have a docstring in the first line
if s[-1] == ')':
start = s.find('(')
if start >= 0:
end = s.find('[')
if end <= 0:
end = s.find(')')
if end <= 0:
end = len(s)
args = s[start:end]
if not args[-1] == ')':
args = args + ')'
# now, get rid of unwanted chars
l = len(args) - 1
r = []
for i in xrange(len(args)):
if i == 0 or i == l:
r.append(args[i])
else:
r.append(check_char(args[i]))
args = ''.join(r)
if IS_IPY:
if args.startswith('(self:'):
i = args.find(',')
if i >= 0:
args = '(self' + args[i:]
else:
args = '(self)'
i = args.find(')')
if i > 0:
args = args[:i + 1]
except:
pass
return args, doc
@@ -1,17 +1,15 @@
import os
from _pydev_bundle.pydev_imports import xmlrpclib, _queue, Exec
import sys
from _pydevd_bundle.pydevd_constants import IS_JYTHON
from _pydev_bundle._pydev_calltip_util import get_description
from _pydev_imps._pydev_saved_modules import thread
from _pydevd_bundle import pydevd_xml
from _pydevd_bundle import pydevd_vars
from _pydevd_bundle import pydevd_xml
from _pydevd_bundle.pydevd_constants import IS_JYTHON
from _pydevd_bundle.pydevd_utils import * # @UnusedWildImport
import traceback
#=======================================================================================================================
# =======================================================================================================================
# Null
#=======================================================================================================================
# =======================================================================================================================
class Null:
"""
Gotten from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
@@ -54,39 +52,39 @@ class Null:
return 0
#=======================================================================================================================
# =======================================================================================================================
# BaseStdIn
#=======================================================================================================================
# =======================================================================================================================
class BaseStdIn:
def __init__(self, original_stdin=sys.stdin, *args, **kwargs):
try:
self.encoding = sys.stdin.encoding
except:
#Not sure if it's available in all Python versions...
# Not sure if it's available in all Python versions...
pass
self.original_stdin = original_stdin
def readline(self, *args, **kwargs):
#sys.stderr.write('Cannot readline out of the console evaluation\n') -- don't show anything
#This could happen if the user had done input('enter number).<-- upon entering this, that message would appear,
#which is not something we want.
# sys.stderr.write('Cannot readline out of the console evaluation\n') -- don't show anything
# This could happen if the user had done input('enter number).<-- upon entering this, that message would appear,
# which is not something we want.
return '\n'
def isatty(self):
return False #not really a file
return False # not really a file
def write(self, *args, **kwargs):
pass #not available StdIn (but it can be expected to be in the stream interface)
pass # not available StdIn (but it can be expected to be in the stream interface)
def flush(self, *args, **kwargs):
pass #not available StdIn (but it can be expected to be in the stream interface)
pass # not available StdIn (but it can be expected to be in the stream interface)
def read(self, *args, **kwargs):
#in the interactive interpreter, a read and a readline are the same.
# in the interactive interpreter, a read and a readline are the same.
return self.readline()
def close(self, *args, **kwargs):
pass #expected in StdIn
pass # expected in StdIn
def __getattr__(self, item):
# it's called if the attribute wasn't found
@@ -95,9 +93,9 @@ class BaseStdIn:
raise AttributeError("%s has no attribute %s" % (self.original_stdin, item))
#=======================================================================================================================
# =======================================================================================================================
# StdIn
#=======================================================================================================================
# =======================================================================================================================
class StdIn(BaseStdIn):
'''
Object to be added to stdin (to emulate it as non-blocking while the next line arrives)
@@ -110,16 +108,18 @@ class StdIn(BaseStdIn):
self.host = host
def readline(self, *args, **kwargs):
#Ok, callback into the client to get the new input
# Ok, callback into the client to get the new input
try:
server = xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port))
requested_input = server.RequestInput()
if not requested_input:
return '\n' #Yes, a readline must return something (otherwise we can get an EOFError on the input() call).
return '\n' # Yes, a readline must return something (otherwise we can get an EOFError on the input() call).
return requested_input
except:
return '\n'
def close(self, *args, **kwargs):
pass # expected in StdIn
#=======================================================================================================================
# DebugConsoleStdIn
@@ -155,9 +155,10 @@ class CodeFragment:
if not code_fragment.is_single_line:
self.is_single_line = False
#=======================================================================================================================
# =======================================================================================================================
# BaseInterpreterInterface
#=======================================================================================================================
# =======================================================================================================================
class BaseInterpreterInterface:
def __init__(self, mainThread):
self.mainThread = mainThread
@@ -206,15 +207,15 @@ class BaseInterpreterInterface:
try:
help = None
if 'pydoc' in sys.modules:
pydoc = sys.modules['pydoc'] #Don't import it if it still is not there.
pydoc = sys.modules['pydoc'] # Don't import it if it still is not there.
if hasattr(pydoc, 'help'):
#You never know how will the API be changed, so, let's code defensively here
# You never know how will the API be changed, so, let's code defensively here
help = pydoc.help
if not hasattr(help, 'input'):
help = None
except:
#Just ignore any error here
# Just ignore any error here
pass
more = False
@@ -222,7 +223,7 @@ class BaseInterpreterInterface:
sys.stdin = self.create_std_in(debugger, original_in)
try:
if help is not None:
#This will enable the help() function to work.
# This will enable the help() function to work.
try:
try:
help.input = sys.stdin
@@ -268,7 +269,6 @@ class BaseInterpreterInterface:
return more
def do_add_exec(self, codeFragment):
'''
Subclasses should override.
@@ -277,7 +277,6 @@ class BaseInterpreterInterface:
'''
raise NotImplementedError()
def get_namespace(self):
'''
Subclasses should override.
@@ -286,66 +285,48 @@ class BaseInterpreterInterface:
'''
raise NotImplementedError()
def __resolve_reference__(self, text):
"""
def getDescription(self, text):
try:
obj = None
if '.' not in text:
:type text: str
"""
obj = None
if '.' not in text:
try:
obj = self.get_namespace()[text]
except KeyError:
pass
if obj is None:
try:
obj = self.get_namespace()[text]
except KeyError:
return ''
else:
try:
splitted = text.split('.')
obj = self.get_namespace()[splitted[0]]
for t in splitted[1:]:
obj = getattr(obj, t)
except:
return ''
if obj is not None:
try:
if sys.platform.startswith("java"):
#Jython
doc = obj.__doc__
if doc is not None:
return doc
from _pydev_bundle import _pydev_jy_imports_tipper
is_method, infos = _pydev_jy_imports_tipper.ismethod(obj)
ret = ''
if is_method:
for info in infos:
ret += info.get_as_doc()
return ret
else:
#Python and Iron Python
import inspect #@UnresolvedImport
doc = inspect.getdoc(obj)
if doc is not None:
return doc
obj = self.get_namespace()['__builtins__'][text]
except:
pass
try:
#if no attempt succeeded, try to return repr()...
return repr(obj)
except:
if obj is None:
try:
#otherwise the class
return str(obj.__class__)
obj = getattr(self.get_namespace()['__builtins__'], text, None)
except:
#if all fails, go to an empty string
return ''
except:
traceback.print_exc()
return ''
pass
else:
try:
last_dot = text.rindex('.')
parent_context = text[0:last_dot]
res = pydevd_vars.eval_in_context(parent_context, self.get_namespace(), self.get_namespace())
obj = getattr(res, text[last_dot + 1:])
except:
pass
return obj
def getDescription(self, text):
try:
obj = self.__resolve_reference__(text)
if obj is None:
return ''
return get_description(obj)
except:
return ''
def do_exec_code(self, code, is_single_line):
try:
@@ -364,7 +345,6 @@ class BaseInterpreterInterface:
def execLine(self, line):
return self.do_exec_code(line, True)
def execMultipleLines(self, lines):
if IS_JYTHON:
for line in lines.split('\n'):
@@ -372,9 +352,8 @@ class BaseInterpreterInterface:
else:
return self.do_exec_code(lines, False)
def interrupt(self):
self.buffer = None # Also clear the buffer when it's interrupted.
self.buffer = None # Also clear the buffer when it's interrupted.
try:
if self.interruptable:
called = False
@@ -412,10 +391,10 @@ class BaseInterpreterInterface:
pass
if not called:
if hasattr(thread, 'interrupt_main'): #Jython doesn't have it
if hasattr(thread, 'interrupt_main'): # Jython doesn't have it
thread.interrupt_main()
else:
self.mainThread._thread.interrupt() #Jython
self.mainThread._thread.interrupt() # Jython
return True
except:
traceback.print_exc()
@@ -495,11 +474,11 @@ class BaseInterpreterInterface:
Used to show console with variables connection.
Always return a frame where the locals map to our internal namespace.
'''
VIRTUAL_FRAME_ID = "1" # matches PyStackFrameConsole.java
VIRTUAL_CONSOLE_ID = "console_main" # matches PyThreadConsole.java
VIRTUAL_FRAME_ID = "1" # matches PyStackFrameConsole.java
VIRTUAL_CONSOLE_ID = "console_main" # matches PyThreadConsole.java
if thread_id == VIRTUAL_CONSOLE_ID and frame_id == VIRTUAL_FRAME_ID:
f = FakeFrame()
f.f_globals = {} #As globals=locals here, let's simply let it empty (and save a bit of network traffic).
f.f_globals = {} # As globals=locals here, let's simply let it empty (and save a bit of network traffic).
f.f_locals = self.get_namespace()
return f
else:
@@ -527,7 +506,7 @@ class BaseInterpreterInterface:
except:
# This happens on Jython embedded in host eclipse
traceback.print_exc()
sys.stderr.write('pydevd is not available, cannot connect\n',)
sys.stderr.write('pydevd is not available, cannot connect\n', )
from _pydev_bundle import pydev_localhost
threading.currentThread().__pydevd_id__ = "console_main"
@@ -589,9 +568,10 @@ class BaseInterpreterInterface:
# it to run in the main thread.
self.exec_queue.put(do_enable_gui)
#=======================================================================================================================
# =======================================================================================================================
# FakeFrame
#=======================================================================================================================
# =======================================================================================================================
class FakeFrame:
'''
Used to show console with variables connection.
@@ -24,17 +24,22 @@ import org.jetbrains.annotations.NotNull;
public class PyResolveContext {
private final boolean myAllowImplicits;
private final boolean myAllowProperties;
private final boolean myAllowRemote;
private final TypeEvalContext myTypeEvalContext;
private PyResolveContext(boolean allowImplicits, boolean allowProperties) {
myAllowImplicits = allowImplicits;
myAllowProperties = allowProperties;
myTypeEvalContext = null;
myAllowRemote = false;
}
private PyResolveContext(boolean allowImplicits, boolean allowProperties, TypeEvalContext typeEvalContext) {
private PyResolveContext(boolean allowImplicits, boolean allowProperties, boolean allowRemote, TypeEvalContext typeEvalContext) {
myAllowImplicits = allowImplicits;
myAllowProperties = allowProperties;
myAllowRemote = allowRemote;
myTypeEvalContext = typeEvalContext;
}
@@ -46,6 +51,10 @@ public class PyResolveContext {
return myAllowProperties;
}
public boolean allowRemote() {
return myAllowRemote;
}
private static final PyResolveContext ourDefaultContext = new PyResolveContext(true, true);
private static final PyResolveContext ourNoImplicitsContext = new PyResolveContext(false, true);
private static final PyResolveContext ourNoPropertiesContext = new PyResolveContext(false, false);
@@ -63,11 +72,15 @@ public class PyResolveContext {
}
public PyResolveContext withTypeEvalContext(@NotNull TypeEvalContext context) {
return new PyResolveContext(myAllowImplicits, myAllowProperties, context);
return new PyResolveContext(myAllowImplicits, myAllowProperties, myAllowRemote, context);
}
public PyResolveContext withoutImplicits() {
return new PyResolveContext(false, myAllowProperties, myTypeEvalContext);
return new PyResolveContext(false, myAllowProperties, myAllowRemote, myTypeEvalContext);
}
public PyResolveContext withRemote() {
return new PyResolveContext(myAllowImplicits, myAllowProperties, true, myTypeEvalContext);
}
public TypeEvalContext getTypeEvalContext() {
@@ -65,7 +65,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
final PyCallExpression callExpr = argumentList.getCallExpression();
if (callExpr != null) {
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(argumentList.getProject(), argumentList.getContainingFile());
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(typeEvalContext);
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withRemote().withTypeEvalContext(typeEvalContext);
final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(resolveContext);
if (mapping.getMarkedCallee() != null) {
context.setItemsToShow(new Object[] { mapping });
@@ -152,7 +152,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
PyPsiUtils.assertValid(callExpression);
// really we need to redo analysis every UI update; findElementForParameterInfo isn't called while typing
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(callExpression.getProject(), callExpression.getContainingFile());
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(typeEvalContext);
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withRemote().withTypeEvalContext(typeEvalContext);
final PyCallExpression.PyArgumentsMapping mapping = callExpression.mapArguments(resolveContext);
final PyMarkedCallee marked = mapping.getMarkedCallee();
if (marked == null) return; // resolution failed
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.console.completion;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
import com.intellij.codeInsight.completion.InsertHandler;
@@ -24,19 +25,18 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiPolyVariantReferenceBase;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
import com.jetbrains.python.console.pydev.IToken;
import com.jetbrains.python.console.pydev.PyCodeCompletionImages;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -46,20 +46,75 @@ import java.util.Map;
* @author oleg
*/
public class PydevConsoleReference extends PsiPolyVariantReferenceBase<PyReferenceExpression> {
private final ConsoleCommunication myCommunication;
private final String myPrefix;
private final boolean myAllowRemoteResolve;
public PydevConsoleReference(final PyReferenceExpression expression,
final ConsoleCommunication communication,
final String prefix) {
final ConsoleCommunication communication, final String prefix, boolean allowRemoteResolve) {
super(expression, true);
myCommunication = communication;
myPrefix = prefix;
myAllowRemoteResolve = allowRemoteResolve;
}
@NotNull
public ResolveResult[] multiResolve(boolean incompleteCode) {
return ResolveResult.EMPTY_ARRAY;
if (!myAllowRemoteResolve) {
return RatedResolveResult.EMPTY_ARRAY;
}
PyExpression pyExpression = resolveFromStub();
if (pyExpression == null) {
return RatedResolveResult.EMPTY_ARRAY;
}
if (pyExpression instanceof PyReferenceExpression) {
final PsiReference redirectedRef = pyExpression.getReference();
if (redirectedRef != null) {
PsiElement resolved = redirectedRef.resolve();
if (resolved != null) {
return new ResolveResult[]{new RatedResolveResult(RatedResolveResult.RATE_HIGH, resolved)};
}
}
}
return RatedResolveResult.EMPTY_ARRAY;
}
public PyElement getDocumentationElement() {
return resolveFromStub();
}
private PyExpression resolveFromStub() {
String qualifiedName = myElement.getText();
if (qualifiedName == null) {
return null;
}
String description;
try {
description = myCommunication.getDescription(qualifiedName);
if (Strings.isNullOrEmpty(description)) {
return null;
}
}
catch (Exception e) {
return null;
}
PyElementGenerator generator = PyElementGenerator.getInstance(myElement.getProject());
PyFile dummyFile = (PyFile)generator.createDummyFile(LanguageLevel.forElement(myElement), description);
Module module = ModuleUtilCore.findModuleForPsiElement(myElement);
if (module != null) {
dummyFile.putUserData(ModuleUtilCore.KEY_MODULE, module);
}
List<PyStatement> statements = dummyFile.getStatements();
PyStatement pyStatement = statements.get(statements.size() - 1);
return pyStatement instanceof PyExpressionStatement ? ((PyExpressionStatement)pyStatement).getExpression() : null;
}
@NotNull
@@ -71,8 +126,7 @@ public class PydevConsoleReference extends PsiPolyVariantReferenceBase<PyReferen
final PsiManager manager = myElement.getManager();
final String name = completion.getName();
final int type = completion.getType();
LookupElementBuilder builder = LookupElementBuilder
.create(new PydevConsoleElement(manager, name, completion.getDescription()))
LookupElementBuilder builder = LookupElementBuilder.create(new PydevConsoleElement(manager, name, completion.getDescription()))
.withIcon(PyCodeCompletionImages.getImageForType(type));
@@ -83,9 +83,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
final ConsoleCommunication communication = file.getCopyableUserData(PydevConsoleRunner.CONSOLE_KEY);
if (communication != null) {
if (qualifier != null) {
return new PydevConsoleReference(this, communication, qualifier.getText() + ".");
return new PydevConsoleReference(this, communication, qualifier.getText() + ".", context.allowRemote());
}
return new PydevConsoleReference(this, communication, "");
return new PydevConsoleReference(this, communication, "", context.allowRemote());
}
if (qualifier != null) {