diff --git a/python/helpers/pycharm/pycharm_load_entry_point.py b/python/helpers/pycharm/pycharm_load_entry_point.py
index ecf4f905080f..5eadeb1a3516 100644
--- a/python/helpers/pycharm/pycharm_load_entry_point.py
+++ b/python/helpers/pycharm/pycharm_load_entry_point.py
@@ -4,6 +4,9 @@ from pkg_resources import load_entry_point
if __name__ == '__main__':
dist = os.environ.get("PYCHARM_EP_DIST")
name = os.environ.get("PYCHARM_EP_NAME")
- sys.exit(
- load_entry_point(dist, "console_scripts", name)()
- )
+ if dist == "ipython" and name == "ipython":
+ from IPython import start_ipython
+ f = start_ipython
+ else:
+ f = load_entry_point(dist, "console_scripts", name)
+ sys.exit(f())
diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py
index 59e78f140254..60ba0ac8d049 100644
--- a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py
+++ b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py
@@ -58,10 +58,9 @@ each command has a format:
* PYDB - pydevd, the python end
'''
-from _pydev_imps import _pydev_threading as threading
+from _pydev_bundle.pydev_imports import _queue
from _pydev_imps import _pydev_time as time, _pydev_thread
from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
-from _pydev_bundle.pydev_imports import _queue
from _pydevd_bundle.pydevd_constants import * #@UnusedWildImport
try:
@@ -115,10 +114,9 @@ CMD_REMOVE_DJANGO_EXCEPTION_BREAK = 126
CMD_SET_NEXT_STATEMENT = 127
CMD_SMART_STEP_INTO = 128
CMD_EXIT = 129
+
CMD_SIGNATURE_CALL_TRACE = 130
-
-
CMD_SET_PY_EXCEPTION = 131
CMD_GET_FILE_CONTENTS = 132
CMD_SET_PROPERTY_TRACE = 133
@@ -142,35 +140,36 @@ CMD_RETURN = 502
CMD_ERROR = 901
ID_TO_MEANING = {
- '101':'CMD_RUN',
- '102':'CMD_LIST_THREADS',
- '103':'CMD_THREAD_CREATE',
- '104':'CMD_THREAD_KILL',
- '105':'CMD_THREAD_SUSPEND',
- '106':'CMD_THREAD_RUN',
- '107':'CMD_STEP_INTO',
- '108':'CMD_STEP_OVER',
- '109':'CMD_STEP_RETURN',
- '110':'CMD_GET_VARIABLE',
- '111':'CMD_SET_BREAK',
- '112':'CMD_REMOVE_BREAK',
- '113':'CMD_EVALUATE_EXPRESSION',
- '114':'CMD_GET_FRAME',
- '115':'CMD_EXEC_EXPRESSION',
- '116':'CMD_WRITE_TO_CONSOLE',
- '117':'CMD_CHANGE_VARIABLE',
- '118':'CMD_RUN_TO_LINE',
- '119':'CMD_RELOAD_CODE',
- '120':'CMD_GET_COMPLETIONS',
- '121':'CMD_CONSOLE_EXEC',
- '122':'CMD_ADD_EXCEPTION_BREAK',
- '123':'CMD_REMOVE_EXCEPTION_BREAK',
- '124':'CMD_LOAD_SOURCE',
- '125':'CMD_ADD_DJANGO_EXCEPTION_BREAK',
- '126':'CMD_REMOVE_DJANGO_EXCEPTION_BREAK',
- '127':'CMD_SET_NEXT_STATEMENT',
- '128':'CMD_SMART_STEP_INTO',
+ '101': 'CMD_RUN',
+ '102': 'CMD_LIST_THREADS',
+ '103': 'CMD_THREAD_CREATE',
+ '104': 'CMD_THREAD_KILL',
+ '105': 'CMD_THREAD_SUSPEND',
+ '106': 'CMD_THREAD_RUN',
+ '107': 'CMD_STEP_INTO',
+ '108': 'CMD_STEP_OVER',
+ '109': 'CMD_STEP_RETURN',
+ '110': 'CMD_GET_VARIABLE',
+ '111': 'CMD_SET_BREAK',
+ '112': 'CMD_REMOVE_BREAK',
+ '113': 'CMD_EVALUATE_EXPRESSION',
+ '114': 'CMD_GET_FRAME',
+ '115': 'CMD_EXEC_EXPRESSION',
+ '116': 'CMD_WRITE_TO_CONSOLE',
+ '117': 'CMD_CHANGE_VARIABLE',
+ '118': 'CMD_RUN_TO_LINE',
+ '119': 'CMD_RELOAD_CODE',
+ '120': 'CMD_GET_COMPLETIONS',
+ '121': 'CMD_CONSOLE_EXEC',
+ '122': 'CMD_ADD_EXCEPTION_BREAK',
+ '123': 'CMD_REMOVE_EXCEPTION_BREAK',
+ '124': 'CMD_LOAD_SOURCE',
+ '125': 'CMD_ADD_DJANGO_EXCEPTION_BREAK',
+ '126': 'CMD_REMOVE_DJANGO_EXCEPTION_BREAK',
+ '127': 'CMD_SET_NEXT_STATEMENT',
+ '128': 'CMD_SMART_STEP_INTO',
'129': 'CMD_EXIT',
+
'130': 'CMD_SIGNATURE_CALL_TRACE',
'131': 'CMD_SET_PY_EXCEPTION',
@@ -184,14 +183,13 @@ ID_TO_MEANING = {
'139': 'CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED',
'140': 'CMD_IGNORE_THROWN_EXCEPTION_AT',
'141': 'CMD_ENABLE_DONT_TRACE',
+ '143': 'CMD_GET_ARRAY',
+ '144': 'CMD_STEP_INTO_MY_CODE',
+ '145': 'CMD_GET_CONCURRENCY_EVENT',
- '501':'CMD_VERSION',
- '502':'CMD_RETURN',
- '901':'CMD_ERROR',
-
- '143':'CMD_GET_ARRAY',
- '144':'CMD_STEP_INTO_MY_CODE',
- '145':'CMD_GET_CONCURRENCY_EVENT',
+ '501': 'CMD_VERSION',
+ '502': 'CMD_RETURN',
+ '901': 'CMD_ERROR',
}
MAX_IO_MSG_SIZE = 1000 #if the io is too big, we'll not send all (could make the debugger too non-responsive)
diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_frame.py b/python/helpers/pydev/_pydevd_bundle/pydevd_frame.py
index b449dee9c89d..f03c41b44173 100644
--- a/python/helpers/pydev/_pydevd_bundle/pydevd_frame.py
+++ b/python/helpers/pydev/_pydevd_bundle/pydevd_frame.py
@@ -13,13 +13,14 @@ from _pydevd_bundle.pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETUR
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, dict_contains, get_thread_id, STATE_RUN, dict_iter_values
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
+
try:
from inspect import CO_GENERATOR
except:
CO_GENERATOR = 0
try:
- from _pydevd_bundle.pydevd_signature import send_signature_call_trace
+ from _pydevd_bundle.pydevd_signature import send_signature_call_trace, send_signature_return_trace
except ImportError:
def send_signature_call_trace(*args, **kwargs):
pass
@@ -80,6 +81,12 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
return self.trace_exception
+ def trace_return(self, frame, event, arg):
+ if event == 'return':
+ main_debugger, filename, info, thread = self._args
+ send_signature_return_trace(main_debugger, frame, filename, arg)
+ return self.trace_return
+
# IFDEF CYTHON
# def should_stop_on_exception(self, frame, str event, arg):
# cdef PyDBAdditionalThreadInfo info;
@@ -274,12 +281,15 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
try:
# print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
info.is_tracing = True
+ need_trace_return = False
if main_debugger._finish_debugging_session:
return None
if event == 'call' and main_debugger.signature_factory:
- send_signature_call_trace(main_debugger, frame, filename)
+ need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
+ if event == 'return' and main_debugger.signature_factory:
+ send_signature_return_trace(main_debugger, frame, filename, arg)
plugin_manager = main_debugger.plugin
@@ -341,7 +351,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if has_exception_breakpoints:
return self.trace_exception
else:
- return None
+ if need_trace_return:
+ return self.trace_return
+ else:
+ return None
else:
#checks the breakpoint to see if there is a context match in some function
@@ -361,7 +374,10 @@ class PyDBFrame: # No longer cdef because object was dying when only a reference
if has_exception_breakpoints:
return self.trace_exception
else:
- return None
+ if need_trace_return:
+ return self.trace_return
+ else:
+ return None
#We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py b/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py
index e529994048d0..98b660f4ff51 100644
--- a/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py
+++ b/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py
@@ -19,6 +19,7 @@ class Signature(object):
self.name = name
self.args = []
self.args_str = []
+ self.return_type = None
def add_arg(self, name, type):
self.args.append((name, type))
@@ -28,32 +29,39 @@ class Signature(object):
return "%s %s(%s)"%(self.file, self.name, ", ".join(self.args_str))
+def get_type_of_value(value, ignore_module_name=('__main__', '__builtin__', 'builtins')):
+ tp = type(value)
+ class_name = tp.__name__
+ if class_name == 'instance': # old-style classes
+ tp = value.__class__
+ class_name = tp.__name__
+
+ if hasattr(tp, '__module__') and tp.__module__ and tp.__module__ not in ignore_module_name:
+ class_name = "%s.%s"%(tp.__module__, class_name)
+
+ return class_name
+
+
class SignatureFactory(object):
def __init__(self):
self._caller_cache = {}
- self._ignore_module_name = ('__main__', '__builtin__', 'builtins')
+ self.cache = CallSignatureCache()
def is_in_scope(self, filename):
return not pydevd_utils.not_in_project_roots(filename)
- def create_signature(self, frame):
+ def create_signature(self, frame, with_args=True):
try:
code = frame.f_code
locals = frame.f_locals
filename, modulename, funcname = self.file_module_function_of(frame)
res = Signature(filename, funcname)
- for i in xrange(0, code.co_argcount):
- name = code.co_varnames[i]
- tp = type(locals[name])
- class_name = tp.__name__
- if class_name == 'instance': # old-style classes
- tp = locals[name].__class__
- class_name = tp.__name__
-
- if hasattr(tp, '__module__') and tp.__module__ and tp.__module__ not in self._ignore_module_name:
- class_name = "%s.%s"%(tp.__module__, class_name)
-
- res.add_arg(name, class_name)
+ if with_args:
+ for i in xrange(0, code.co_argcount):
+ name = code.co_varnames[i]
+ class_name = get_type_of_value(locals[name])
+
+ res.add_arg(name, class_name)
return res
except:
import traceback
@@ -109,6 +117,33 @@ class SignatureFactory(object):
return filename, modulename, funcname
+
+def get_signature_info(signature):
+ return signature.file, signature.name, ' '.join([arg[1]for arg in signature.args])
+
+
+def get_frame_info(frame):
+ co = frame.f_code
+ return co.co_name, frame.f_lineno, co.co_filename
+
+
+class CallSignatureCache(object):
+ def __init__(self):
+ self.cache = {}
+
+ def add(self, signature):
+ filename, name, args_type = get_signature_info(signature)
+ calls_from_file = self.cache.setdefault(filename, {})
+ name_calls = calls_from_file.setdefault(name, {})
+ name_calls[args_type] = None
+
+ def is_in_cache(self, signature):
+ filename, name, args_type = get_signature_info(signature)
+ if args_type in self.cache.get(filename, {}).get(name, {}):
+ return True
+ return False
+
+
def create_signature_message(signature):
cmdTextList = ["
");
- escaped = !escaped;
- //noinspection AssignmentToForLoopParameter
- i += 2;
- continue;
- }
- }
- if (escaped && charAt == '\n') {
- markdown.append("
");
- continue;
- }
-
- if (!escaped && !backQuoted) {
- if (charAt == '$' && source.length() > i + 1 && source.charAt(i+1) != '$') {
- inCode = !inCode;
- }
-
- if (charAt == '\\' && source.substring(i).startsWith("\\begin")) {
- inMultiStringCode += 1;
- }
- if (charAt == '\\' && source.substring(i).startsWith("\\end")) {
- inEnd = true;
- }
- }
-
- final boolean doubleDollar = charAt == '$' && ((source.length() > i + 1 && source.charAt(i + 1) == '$')
- || (i >= 1 && source.charAt(i - 1) == '$'));
- if (inCode || inMultiStringCode != 0 || (!backQuoted && doubleDollar)) {
- if (markdown.length() != 0) {
- result.append(markdown.toString());
- markdown = new StringBuilder();
- }
- formula.append(charAt);
- }
- else {
- if (formula.length() != 0 && charAt == '$') {
- formula.append(charAt);
- }
- else {
- markdown.append(charAt);
- }
- if (formula.length() != 0) {
- addFormula(formula.toString(), document, imageIndex, result);
-
- imageIndex += 1;
- formula = new StringBuilder();
- }
- }
-
- if (inEnd && charAt == '}') {
- inMultiStringCode -= 1;
- inEnd = false;
- }
-
+ int start = 0;
+ int end = StringUtil.indexOf(source, "```");
+ while (end > 0) {
+ result.append(source.substring(start, end));
+ result.append(escaped? "" : "");
+ escaped = !escaped;
+ start = end + 3;
+ end = StringUtil.indexOf(source, "```", end + 1);
}
- if (formula.length() != 0) {
- addFormula(formula.toString(), document, imageIndex, result);
- }
- if (markdown.length() != 0) {
- result.append(markdown.toString());
- }
- return markdown2Html(result.toString());
+ result.append(source.substring(start));
+
+ return result.toString();
}
- private static void addFormula(@NotNull final String formulaText, @NotNull final HTMLDocument editorDocument, int imageIndex, StringBuilder result) {
- final SnuggleEngine engine = new SnuggleEngine();
- engine.getPackages().add(0, IpnbTexPackageDefinitions.getPackage());
+ @NotNull
+ private static String replaceLinks(@NotNull String source) {
+ final Pattern inlineLink = Pattern.compile("(\\[(.*?)\\]\\([ \\t]*(.*?)>?[ \\t]*(([\'\"])(.*?)\\5)?\\))", 32);
+ final Matcher matcher = inlineLink.matcher(source);
+ final StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ String linkText = matcher.group(2);
+ String url = matcher.group(3);
+ String title = matcher.group(6);
+ StringBuilder link = new StringBuilder();
+ link.append("").append(linkText);
+ link.append("");
+ matcher.appendReplacement(sb, link.toString());
+ }
+ matcher.appendTail(sb);
- final SnuggleInput input = new SnuggleInput(formulaText);
+ source = sb.toString();
+ return source;
+ }
+ private static void initHyperlinkListener(@NotNull final WebEngine engine) {
+ engine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
+ if (newState == Worker.State.SUCCEEDED) {
+ final EventListener listener = new HyperlinkListener(engine);
+ addListenerToAllHyperlinkItems(engine, listener);
+ }
+ });
+ }
+
+ private static void addListenerToAllHyperlinkItems(WebEngine engine, EventListener listener) {
+ final Document doc = engine.getDocument();
+ if (doc != null) {
+ final NodeList nodeList = doc.getElementsByTagName("a");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ ((EventTarget)nodeList.item(i)).addEventListener("click", listener, false);
+ }
+ }
+ }
+
+ private static class HyperlinkListener implements EventListener {
+ @NotNull private final WebEngine myEngine;
+
+ public HyperlinkListener(@NotNull final WebEngine engine) {
+ myEngine = engine;
+ }
+
+ @Override
+ public void handleEvent(Event ev) {
+ String domEventType = ev.getType();
+ if (domEventType.equals("click")) {
+ myEngine.setJavaScriptEnabled(true);
+ myEngine.getLoadWorker().cancel();
+ ev.preventDefault();
+
+ UIUtil.invokeLaterIfNeeded(() -> {
+
+ final String href = ((Element)ev.getTarget()).getAttribute("href");
+ if (href == null) return;
+ final URI address;
+ try {
+ address = new URI(href);
+ BrowserUtil.browse(address);
+ }
+ catch (URISyntaxException e) {
+ LOG.warn(e.getMessage());
+ }
+ });
+
+ }
+ }
+ }
+
+ private static void adjustHeight(final WebView webView, final JFXPanel javafxPanel, String source) {
try {
- session.parseInput(input);
- XMLStringOutputOptions options = new XMLStringOutputOptions();
- options.setIndenting(true);
- options.setAddingMathSourceAnnotations(false);
- final String xmlString = session.buildXMLString(options);
- if (xmlString == null) return;
- final LayoutContextImpl context = (LayoutContextImpl)LayoutContextImpl.getDefaultLayoutContext();
- context.setParameter(Parameter.MATHCOLOR, UIUtil.getTextAreaForeground());
- context.setParameter(Parameter.MATHSIZE, 18);
+ Object result = webView.getEngine().executeScript("document.getElementById(\"mydiv\").offsetHeight");
+ if (result instanceof Integer) {
+ final int fontSize = EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize();
+ double x = (double)source.length() * 8 / (int)result;
+ final double height = (source.length() * fontSize) / x + 20;
+ final int width = (int)(webView.getWidth() == 0 ? 1500 : webView.getWidth());
+ final Dimension size = new Dimension(width, (int)height);
- final Document document = MathMLParserSupport.parseString(xmlString);
-
- final BufferedImage image = Converter.getInstance().render(document, context);
-
- try {
- @SuppressWarnings("unchecked")
- Dictionary cache = (Dictionary)editorDocument.getProperty("imageCache");
- if (cache == null) {
- //noinspection UseOfObsoleteCollectionType
- cache = new Hashtable();
- editorDocument.putProperty("imageCache", cache);
- }
-
- final URL u = new URL(ourImagePrefix + imageIndex + ".jpg");
- cache.put(u, image);
- }
- catch (MalformedURLException e) {
- LOG.error(e);
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ javafxPanel.setPreferredSize(size);
+ javafxPanel.revalidate();
+ javafxPanel.repaint();
+ }
+ });
}
}
- catch (IOException e) {
- LOG.error(e);
+ catch (JSException ignore) {
}
- catch (SAXException e) {
- LOG.error(e);
- LOG.info("formula text " + formulaText);
+ }
+
+ private static void updateLaf(boolean isDarcula, BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
+ if (isDarcula) {
+ updateLafDarcula(pane, engine, jfxPanel);
}
- catch (ParserConfigurationException e) {
- LOG.error(e);
- }
- if (formulaText.startsWith("$$") || formulaText.startsWith("\\begin")) {
- result.append(".append(ourImagePrefix).append(imageIndex).append(".jpg\"/)
");
- }
- else
- result.append("
");
+ }
+
+ private static void updateLafDarcula(BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
+ Platform.runLater(() -> {
+ ourStyleUrl = IpnbFileType.class.getResource("/style/javaFXBrowserDarcula.css");
+ engine.setUserStyleSheetLocation(ourStyleUrl.toExternalForm());
+ pane.setStyle("-fx-background-color: #313335");
+ jfxPanel.getScene().getStylesheets().add(ourStyleUrl.toExternalForm());
+ engine.reload();
+ });
}
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/IpnbFileEditor.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/IpnbFileEditor.java
index b72dd2c1403d..c392c7f0786d 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/IpnbFileEditor.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/IpnbFileEditor.java
@@ -183,7 +183,7 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor {
public void actionPerformed(ActionEvent e) {
IpnbReloadKernelAction.reloadKernel(IpnbFileEditor.this);
}
- }, AllIcons.Actions.Refresh, "Restart kernel");
+ }, AllIcons.Actions.Refresh, "Restart Kernel");
}
private void addSaveButton(@NotNull final JPanel controlPanel) {
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/actions/IpnbCreateFileAction.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/actions/IpnbCreateFileAction.java
new file mode 100644
index 000000000000..189ee133571d
--- /dev/null
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/actions/IpnbCreateFileAction.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2016 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 org.jetbrains.plugins.ipnb.editor.actions;
+
+import com.intellij.ide.actions.CreateFileFromTemplateAction;
+import com.intellij.ide.actions.CreateFileFromTemplateDialog;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.plugins.ipnb.IpnbFileType;
+
+public class IpnbCreateFileAction extends CreateFileFromTemplateAction implements DumbAware {
+ public IpnbCreateFileAction() {
+ super("IPython Notebook", "Creates an IPython Notebook file from the specified template", IpnbFileType.INSTANCE.getIcon());
+ }
+
+ @Override
+ protected void buildDialog(Project project, PsiDirectory directory, CreateFileFromTemplateDialog.Builder builder) {
+ builder
+ .setTitle("New IPython Notebook")
+ .addKind("IPython Notebook", IpnbFileType.INSTANCE.getIcon(), "IPython Notebook");
+ }
+
+ @Override
+ protected String getActionName(PsiDirectory directory, String newName, String templateName) {
+ return "Create IPython Notebook " + newName;
+ }
+}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
index 0fd8853ed919..fb0a77a7ab44 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
@@ -162,7 +162,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
myIpnbPanels.add(panel);
}
else if (cell instanceof IpnbMarkdownCell) {
- panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
+ panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell, this);
addComponent(panel);
}
else if (cell instanceof IpnbHeadingCell) {
@@ -369,7 +369,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
panel = new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)cell);
}
else if (cell instanceof IpnbMarkdownCell) {
- panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
+ panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell, myParent.getIpnbFilePanel());
}
else if (cell instanceof IpnbHeadingCell) {
panel = new IpnbHeadingPanel((IpnbHeadingCell)cell);
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbHeadingPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbHeadingPanel.java
index 38c5cc4512f0..b5874d4a72ce 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbHeadingPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbHeadingPanel.java
@@ -1,5 +1,6 @@
package org.jetbrains.plugins.ipnb.editor.panels;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.ui.components.JBLabel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
@@ -18,7 +19,8 @@ public class IpnbHeadingPanel extends IpnbEditablePanel" + myCell.getSourceAsString() + " " + myCell.getSourceAsString() + " ";
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbMarkdownPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbMarkdownPanel.java
index 918b22a2e08c..c462a6a2b24b 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbMarkdownPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbMarkdownPanel.java
@@ -6,10 +6,13 @@ import org.jetbrains.plugins.ipnb.format.cells.IpnbMarkdownCell;
import javax.swing.*;
-public class IpnbMarkdownPanel extends IpnbEditablePanel {
+public class IpnbMarkdownPanel extends IpnbEditablePanel {
- public IpnbMarkdownPanel(@NotNull final IpnbMarkdownCell cell) {
+ private final IpnbFilePanel myParent;
+
+ public IpnbMarkdownPanel(@NotNull final IpnbMarkdownCell cell, @NotNull final IpnbFilePanel parent) {
super(cell);
+ myParent = parent;
initPanel();
}
@@ -19,8 +22,9 @@ public class IpnbMarkdownPanel extends IpnbEditablePanel content = token.getArguments()[0].getContents();
- Element result = builder.appendMathMLElement(parentElement, "mover");
- builder.handleMathTokensAsSingleElement(result, content);
- builder.appendMathMLOperatorElement(result, "\u02D9");
- }
- }, null);
- ourPackage.addComplexCommandSameArgMode("pi", false, 0, MATH_MODE_ONLY, new CommandHandler() {
- @Override
- public void handleCommand(DOMBuilder builder, Element parentElement, CommandToken token) throws SnuggleParseException {
- builder.appendMathMLOperatorElement(parentElement, "\u03c0");
- }
- }, null);
- ourPackage.addEnvironment("eqnarray", ALL_MODES, MATH, Interpretation.TABULAR, new EqnArrayHandler(), START_NEW_XHTML_BLOCK);
- ourPackage.addSimpleMathCommand("lt", new MathOperatorInterpretation("<"));
- ourPackage.addSimpleMathCommand("gt", new MathOperatorInterpretation(">"));
-
- ourPackage.loadMathCharacterDefinitions(ourMathCharacters);
- }
-}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodeOutputPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodeOutputPanel.java
index 4b65051a71bd..1bf847c81c6f 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodeOutputPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodeOutputPanel.java
@@ -1,7 +1,10 @@
package org.jetbrains.plugins.ipnb.editor.panels.code;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
+import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
@@ -9,9 +12,11 @@ import javax.swing.*;
import java.awt.*;
public class IpnbCodeOutputPanel extends IpnbPanel {
+ protected final IpnbFilePanel myParent;
- public IpnbCodeOutputPanel(@NotNull final K cell) {
+ public IpnbCodeOutputPanel(@NotNull final K cell, @Nullable final IpnbFilePanel parent) {
super(cell, new BorderLayout());
+ myParent = parent;
myViewPanel = createViewPanel();
add(myViewPanel);
}
@@ -20,6 +25,9 @@ public class IpnbCodeOutputPanel extends IpnbPanel {
}
else if (outputCell instanceof IpnbHtmlOutputCell) {
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
- new IpnbHtmlPanel((IpnbHtmlOutputCell)outputCell));
+ new IpnbHtmlPanel((IpnbHtmlOutputCell)outputCell, myParent.getIpnbFilePanel()));
}
else if (outputCell instanceof IpnbLatexOutputCell) {
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
- new IpnbLatexPanel((IpnbLatexOutputCell)outputCell));
+ new IpnbLatexPanel((IpnbLatexOutputCell)outputCell, myParent.getIpnbFilePanel()));
}
else if (outputCell instanceof IpnbErrorOutputCell) {
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
@@ -101,7 +101,7 @@ public class IpnbCodePanel extends IpnbEditablePanel {
}
else if (outputCell.getSourceAsString() != null) {
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
- new IpnbCodeOutputPanel(outputCell));
+ new IpnbCodeOutputPanel(outputCell, myParent.getIpnbFilePanel()));
}
mainPanel.add(panel);
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbErrorPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbErrorPanel.java
index fca3dd12eca4..34a5407aa539 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbErrorPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbErrorPanel.java
@@ -1,5 +1,6 @@
package org.jetbrains.plugins.ipnb.editor.panels.code;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import org.jetbrains.annotations.NotNull;
@@ -16,7 +17,7 @@ import java.util.List;
public class IpnbErrorPanel extends IpnbCodeOutputPanel {
public IpnbErrorPanel(@NotNull final IpnbErrorOutputCell cell) {
- super(cell);
+ super(cell, null);
}
@Override
@@ -24,6 +25,9 @@ public class IpnbErrorPanel extends IpnbCodeOutputPanel {
final List text = myCell.getText();
if (text == null) return new JLabel();
ColorPane ansiColoredPane = new ColorPane();
+ final Font font = ansiColoredPane.getFont();
+ final Font newFont = new Font(font.getName(), font.getStyle(), EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
+ ansiColoredPane.setFont(newFont);
ansiColoredPane.appendANSI(StringUtil.join(text, ""));
ansiColoredPane.setBackground(IpnbEditorUtil.getBackground());
ansiColoredPane.setEditable(false);
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbHtmlPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbHtmlPanel.java
index 1e10546a9002..afce5d4567bf 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbHtmlPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbHtmlPanel.java
@@ -1,29 +1,22 @@
package org.jetbrains.plugins.ipnb.editor.panels.code;
-import com.intellij.ui.components.JBLabel;
+import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
+import org.jetbrains.plugins.ipnb.IpnbUtils;
+import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbHtmlOutputCell;
import javax.swing.*;
public class IpnbHtmlPanel extends IpnbCodeOutputPanel {
- public IpnbHtmlPanel(@NotNull final IpnbHtmlOutputCell cell) {
- super(cell);
+
+ public IpnbHtmlPanel(@NotNull final IpnbHtmlOutputCell cell, @NotNull final IpnbFilePanel parent) {
+ super(cell, parent);
}
@Override
protected JComponent createViewPanel() {
- final StringBuilder text = new StringBuilder("");
- for (String html : myCell.getHtmls()) {
- html = html.replace("\"", "'");
- text.append(html);
- }
- text.append("");
-
- final JBLabel label = new JBLabel(text.toString());
- label.setBackground(IpnbEditorUtil.getBackground());
- label.setOpaque(true);
- return label;
+ final int width = myParent.getWidth();
+ return IpnbUtils.createLatexPane(StringUtil.join(myCell.getHtmls(), ""), width);
}
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbImagePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbImagePanel.java
index c57528359fa8..efd115cb9caf 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbImagePanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbImagePanel.java
@@ -3,10 +3,10 @@ package org.jetbrains.plugins.ipnb.editor.panels.code;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.components.JBLabel;
-import com.intellij.util.Base64;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbImageOutputCell;
+import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;
import javax.swing.*;
@@ -17,7 +17,7 @@ public class IpnbImagePanel extends IpnbCodeOutputPanel {
private static final Logger LOG = Logger.getInstance(IpnbImagePanel.class);
public IpnbImagePanel(@NotNull final IpnbImageOutputCell cell) {
- super(cell);
+ super(cell, null);
}
@Override
@@ -27,7 +27,7 @@ public class IpnbImagePanel extends IpnbCodeOutputPanel {
final JBLabel label = new JBLabel();
if (!StringUtil.isEmptyOrSpaces(png)) {
try {
- byte[] btDataFile = Base64.decode(png);
+ byte[] btDataFile = new BASE64Decoder().decodeBuffer(png);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(btDataFile));
label.setIcon(new ImageIcon(image));
}
@@ -41,4 +41,4 @@ public class IpnbImagePanel extends IpnbCodeOutputPanel {
return label;
}
-}
\ No newline at end of file
+}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbLatexPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbLatexPanel.java
index a8eab10e9a93..c8dbe9044c4d 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbLatexPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbLatexPanel.java
@@ -5,19 +5,22 @@ import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.IpnbUtils;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
+import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbLatexOutputCell;
import javax.swing.*;
public class IpnbLatexPanel extends IpnbCodeOutputPanel {
- public IpnbLatexPanel(@NotNull final IpnbLatexOutputCell cell) {
- super(cell);
+
+ public IpnbLatexPanel(@NotNull final IpnbLatexOutputCell cell, @NotNull final IpnbFilePanel parent) {
+ super(cell, parent);
setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP));
setBackground(IpnbEditorUtil.getBackground());
}
@Override
protected JComponent createViewPanel() {
- return IpnbUtils.createLatexPane(StringUtil.join(myCell.getLatex(), ""));
+ final int width = myParent.getWidth();
+ return IpnbUtils.createLatexPane(StringUtil.join(myCell.getLatex(), ""), width);
}
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbStreamPanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbStreamPanel.java
index 06f2c8d0d7ef..6b068f5e0dfe 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbStreamPanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbStreamPanel.java
@@ -8,7 +8,7 @@ import javax.swing.*;
public class IpnbStreamPanel extends IpnbCodeOutputPanel {
public IpnbStreamPanel(@NotNull final IpnbStreamOutputCell cell) {
- super(cell);
+ super(cell, null);
}
@Override
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java b/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java
index a75887aba711..9d13e6421554 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/PySignature.java
@@ -13,6 +13,8 @@ import java.util.List;
public class PySignature {
private final String myFile;
private final String myFunctionName;
+
+ private NamedParameter myReturnType = null;
private final List myArgs = Lists.newArrayList();
@@ -46,6 +48,22 @@ public class PySignature {
return myArgs;
}
+ public NamedParameter getReturnType() {
+ return myReturnType;
+ }
+
+ public PySignature addReturnType(@Nullable String returnType) {
+ if (StringUtil.isNotEmpty(returnType)) {
+ if (myReturnType != null) {
+ myReturnType.addType(returnType);
+ }
+ else {
+ myReturnType = new NamedParameter("", returnType);
+ }
+ }
+ return this;
+ }
+
@NotNull
public PySignature addAllArgs(@NotNull PySignature signature) {
for (NamedParameter param : signature.getArgs()) {
@@ -71,6 +89,11 @@ public class PySignature {
return null;
}
+ @Nullable
+ public String getReturnTypeQualifiedName() {
+ return myReturnType != null ? myReturnType.getTypeQualifiedName() : null;
+ }
+
public static class NamedParameter {
private final String myName;
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java
index 586d150caf79..94e4adb12a41 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java
@@ -30,6 +30,7 @@ public abstract class AbstractCommand {
public static final int LOAD_SOURCE = 124;
public static final int SMART_STEP_INTO = 128;
public static final int EXIT = 129;
+
public static final int CALL_SIGNATURE_TRACE = 130;
public static final int CMD_SET_PY_EXCEPTION = 131;
@@ -45,6 +46,9 @@ public abstract class AbstractCommand {
public static final int CMD_ENABLE_DONT_TRACE = 141;
public static final int SHOW_CONSOLE = 142;
+ public static final int GET_ARRAY = 143;
+ public static final int STEP_INTO_MY_CODE = 144;
+ public static final int LOG_CONCURRENCY_EVENT = 145;
public static final int ERROR = 901;
@@ -52,9 +56,7 @@ public abstract class AbstractCommand {
public static final String NEW_LINE_CHAR = "@_@NEW_LINE_CHAR@_@";
public static final String TAB_CHAR = "@_@TAB_CHAR@_@";
- public static final int GET_ARRAY = 143;
- public static final int STEP_INTO_MY_CODE = 144;
- public static final int LOG_CONCURRENCY_EVENT = 145;
+
@NotNull private final RemoteDebugger myDebugger;
private final int myCommandCode;
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java
index cbfe858b2dad..ea4d5173b31b 100644
--- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java
+++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java
@@ -31,10 +31,15 @@ public class ProtocolParser {
while (reader.hasMoreChildren()) {
reader.moveDown();
- if (!"arg".equals(reader.getNodeName())) {
- throw new PyDebuggerException("Expected , found " + reader.getNodeName());
+ if ("arg".equals(reader.getNodeName())) {
+ signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
+ } else
+ if ("return".equals(reader.getNodeName())) {
+ signature.addReturnType(readString(reader, "type", ""));
+ } else {
+ throw new PyDebuggerException("Expected or , found " + reader.getNodeName());
}
- signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
+
reader.moveUp();
}
diff --git a/python/resources/inspectionDescriptions/PyMissingTypeHintsInspection.html b/python/resources/inspectionDescriptions/PyMissingTypeHintsInspection.html
new file mode 100644
index 000000000000..7d88ef248c2c
--- /dev/null
+++ b/python/resources/inspectionDescriptions/PyMissingTypeHintsInspection.html
@@ -0,0 +1,7 @@
+
+
+
+This inspection detects lack of type hints for function declaration in
+ one of the two formats: parameter annotations or a type comment
+
+
\ No newline at end of file
diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template
new file mode 100644
index 000000000000..19a3444d8d77
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/after.html.template
@@ -0,0 +1,2 @@
+def foo(var: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template
new file mode 100644
index 000000000000..12d4a09279d0
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/before.html.template
@@ -0,0 +1,2 @@
+def foo(var):
+ pass
\ No newline at end of file
diff --git a/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html
new file mode 100644
index 000000000000..df913398c765
--- /dev/null
+++ b/python/resources/intentionDescriptions/PyAnnotateTypesIntention/description.html
@@ -0,0 +1,9 @@
+
+
+
+ This intention used to specify annotations for parameters and return type of a function.
+
+ If there is type information collected in run-time, it is used to set the default values of types.
+
+
+
\ No newline at end of file
diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 753f27d8b397..673aa19d6801 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -185,6 +185,11 @@
Python
+
+ com.jetbrains.python.codeInsight.intentions.PyAnnotateTypesIntention
+ Python
+
+
com.jetbrains.python.codeInsight.intentions.TypeAssertionIntention
Python
@@ -375,6 +380,8 @@
+
+
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 51fd13b7e041..1c97f3f02175 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -258,6 +258,9 @@ INTN.add.parameters.to.docstring=Add parameters to docstring
INTN.specify.type.in.annotation=Specify type for reference using annotation
INTN.specify.return.type.in.annotation=Specify return type using annotation
+#PyAnnotateTypesIntention
+INTN.annotate.types=Annotate types
+
#TypeAssertionIntention
INTN.insert.assertion=Insert type assertion
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
new file mode 100644
index 000000000000..72eadd1fad2f
--- /dev/null
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyAnnotateTypesIntention.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2000-2016 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.codeInsight.intentions;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.intellij.codeInsight.CodeInsightUtilCore;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInsight.template.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.documentation.doctest.PyDocstringFile;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static com.jetbrains.python.codeInsight.intentions.SpecifyTypeInPy3AnnotationsIntention.*;
+import static com.jetbrains.python.codeInsight.intentions.TypeIntention.getCallable;
+import static com.jetbrains.python.codeInsight.intentions.TypeIntention.resolvesToFunction;
+
+/**
+ * @author traff
+ */
+public class PyAnnotateTypesIntention implements IntentionAction {
+ private String myText = PyBundle.message("INTN.annotate.types");
+
+ @NotNull
+ public String getText() {
+ return myText;
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return PyBundle.message("INTN.annotate.types");
+ }
+
+ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+ if (!(file instanceof PyFile) || file instanceof PyDocstringFile) return false;
+
+ updateText();
+
+ final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset());
+ if (elementAt == null) return false;
+
+ if (resolvesToFunction(elementAt, new Function() {
+ @Override
+ public Boolean apply(PyFunction input) {
+ return true;
+ }
+ })) {
+ updateText();
+ return true;
+ }
+ return false;
+ }
+
+ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
+ final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset());
+ final PyCallable callable = getCallable(elementAt);
+
+ annotateTypes(editor, callable);
+ }
+
+ public static void annotateTypes(Editor editor, PyCallable callable) {
+ if (isPy3k(callable.getContainingFile())) {
+ generatePy3kTypeAnnotations(callable.getProject(), editor, callable);
+ }
+ else {
+ if (callable instanceof PyFunction) {
+ generateTypeCommentAnnotations(callable.getProject(), (PyFunction)callable);
+ }
+ }
+ }
+
+ private static void generateTypeCommentAnnotations(Project project, PyFunction function) {
+
+ StringBuilder replacementTextBuilder = new StringBuilder("# type: (");
+
+ PyParameter[] params = function.getParameterList().getParameters();
+
+ List> templates = Lists.newArrayList();
+
+ for (int i = 0; i < params.length; i++) {
+ String type = parameterType(params[i]);
+
+ templates.add(Pair.create(replacementTextBuilder.length(), type));
+
+ replacementTextBuilder.append(type);
+
+ if (i < params.length - 1) {
+ replacementTextBuilder.append(", ");
+ }
+ }
+
+ replacementTextBuilder.append(") -> ");
+
+ String returnType = returnType(function);
+ templates.add(Pair.create(replacementTextBuilder.length(), returnType));
+
+ replacementTextBuilder.append(returnType);
+
+ final PyStatementList statements = function.getStatementList();
+ final String indentation = PyIndentUtil.getExpectedElementIndent(statements);
+ replacementTextBuilder.insert(0, indentation);
+ replacementTextBuilder.insert(0, "\n");
+
+
+ final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
+ final Document document = manager.getDocument(function.getContainingFile());
+
+ if (document != null) {
+ final PsiElement beforeStatements = statements.getPrevSibling();
+ int offset = beforeStatements.getTextRange().getStartOffset();
+ if (":".equals(beforeStatements.getText())) {
+ offset += 1;
+ }
+ try {
+ document.insertString(offset, replacementTextBuilder.toString());
+ }
+ finally {
+ manager.commitDocument(document);
+ }
+
+
+ function = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(function);
+
+ if (function != null) {
+ final TemplateBuilder builder =
+ TemplateBuilderFactory.getInstance().createTemplateBuilder(function);
+
+ for (Pair template : templates) {
+ builder.replaceRange(TextRange.from(
+ offset - function.getTextRange().getStartOffset() + replacementTextBuilder.toString().indexOf('#') + template.first,
+ template.second.length()), template.second);
+ }
+
+ startTemplate(project, function, builder);
+ }
+ }
+ }
+
+ private static void startTemplate(Project project, PyCallable callable, TemplateBuilder builder) {
+ final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
+ ;
+
+ int offset = callable.getTextRange().getStartOffset();
+
+ final OpenFileDescriptor descriptor = new OpenFileDescriptor(
+ project,
+ callable.getContainingFile().getVirtualFile(),
+ offset
+ );
+ final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
+ if (targetEditor != null) {
+ targetEditor.getCaretModel().moveToOffset(offset);
+ TemplateManager.getInstance(project).startTemplate(targetEditor, template);
+ }
+ }
+
+ private static boolean isPy3k(PsiFile file) {
+ return LanguageLevel.forElement(file).isPy3K();
+ }
+
+ private static void generatePy3kTypeAnnotations(@NotNull Project project, Editor editor, PyCallable callable) {
+ final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(callable);
+
+ if (callable instanceof PyFunction) {
+ PyExpression returnType = annotateReturnType(project, (PyFunction) callable, false);
+
+ if (returnType != null) {
+ builder.replaceElement(returnType, returnType.getText());
+ }
+ }
+
+ if (callable instanceof PyFunction) {
+ PyFunction function = (PyFunction)callable;
+ PyParameter[] params = function.getParameterList().getParameters();
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ if (params[i] instanceof PyNamedParameter) {
+ params[i] = annotateParameter(project, editor, (PyNamedParameter)params[i], false);
+ }
+ }
+
+
+ for (int i = params.length - 1; i >= 0; i--) {
+ if (params[i] instanceof PyNamedParameter) {
+ params[i] = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(params[i]);
+ PyAnnotation annotation = ((PyNamedParameter)params[i]).getAnnotation();
+ if (annotation != null) {
+ PyExpression annotationValue = annotation.getValue();
+ if (annotationValue != null) {
+ builder.replaceElement(annotationValue, annotationValue.getText());
+ }
+ }
+ }
+ }
+ }
+ if (callable != null) {
+ startTemplate(project, callable, builder);
+ }
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return true;
+ }
+
+
+ protected void updateText() {
+ myText = PyBundle.message("INTN.annotate.types");
+ }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java
index e06bedea1f00..f270def805c3 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInDocstringIntention.java
@@ -25,6 +25,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.PyNames;
import com.jetbrains.python.debugger.PySignature;
import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
@@ -78,7 +79,7 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
}
final PyDocstringGenerator docstringGenerator = PyDocstringGenerator.forDocStringOwner(pyFunction);
- String type = "object";
+ String type = PyNames.OBJECT;
if (param != null) {
final String paramName = StringUtil.notNullize(param.getName());
final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction);
@@ -88,6 +89,10 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
docstringGenerator.withParamTypedByName(param, type);
}
else {
+ final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction);
+ if (signature != null) {
+ type = ObjectUtils.chooseNotNull(signature.getReturnTypeQualifiedName(), type);
+ }
docstringGenerator.withReturnValue(type);
}
@@ -97,7 +102,7 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
@Override
protected void updateText(boolean isReturn) {
- myText = isReturn ? PyBundle.message("INTN.specify.return.type") : PyBundle.message("INTN.specify.type");
+ myText = PyBundle.message(isReturn ? "INTN.specify.return.type" : "INTN.specify.type");
}
@Override
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
index dd3f1aae4f75..f9b41ab1c7b7 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java
@@ -28,17 +28,21 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
+import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.ObjectUtils;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
+import com.jetbrains.python.debugger.PySignature;
+import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
/**
* User: ktisha
- *
+ *
* Helps to specify type in annotations in python3
*/
public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
@@ -69,78 +73,120 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
final PyNamedParameter parameter = getParameter(problemElement, resolved);
if (parameter != null) {
- annotateParameter(project, editor, parameter);
+ annotateParameter(project, editor, parameter, true);
}
else {
- annotateReturnType(project, editor.getDocument(), elementAt);
+ PyCallable callable = getCallable(elementAt);
+ if (callable instanceof PyFunction) {
+ annotateReturnType(project, (PyFunction)callable, true);
+ }
}
}
- private static void annotateParameter(Project project, Editor editor, @NotNull PyNamedParameter parameter) {
+ static PyNamedParameter annotateParameter(Project project,
+ Editor editor,
+ @NotNull PyNamedParameter parameter,
+ boolean createTemplate) {
final PyExpression defaultParamValue = parameter.getDefaultValue();
- final String name = StringUtil.notNullize(parameter.getName());
+ final String paramName = StringUtil.notNullize(parameter.getName());
final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
final String defaultParamText = defaultParamValue == null ? null : defaultParamValue.getText();
- final PyNamedParameter namedParameter = elementGenerator.createParameter(name, defaultParamText, PyNames.OBJECT,
+
+ String paramType = parameterType(parameter);
+
+
+ final PyNamedParameter namedParameter = elementGenerator.createParameter(paramName, defaultParamText, paramType,
LanguageLevel.forElement(parameter));
assert namedParameter != null;
parameter = (PyNamedParameter)parameter.replace(namedParameter);
parameter = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(parameter);
editor.getCaretModel().moveToOffset(parameter.getTextOffset());
final PyAnnotation annotation = parameter.getAnnotation();
- if (annotation != null) {
+ if (annotation != null && createTemplate) {
final PyExpression annotationValue = annotation.getValue();
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(parameter);
assert annotationValue != null : "Generated parameter must have annotation";
final int replacementStart = annotation.getStartOffsetInParent() + annotationValue.getStartOffsetInParent();
builder.replaceRange(TextRange.create(replacementStart,
- replacementStart + annotationValue.getTextLength()), PyNames.OBJECT);
+ replacementStart + annotationValue.getTextLength()), paramType);
final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
TemplateManager.getInstance(project).startTemplate(editor, template);
}
+
+ return parameter;
}
- private void annotateReturnType(Project project, Document document, PsiElement resolved) {
- PyCallable callable = getCallable(resolved);
+ static String parameterType(PyParameter parameter) {
+ String paramType = PyNames.OBJECT;
- if (callable instanceof PyFunction) {
- final String annotationText = " -> " + PyNames.OBJECT;
-
- final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(((PyFunction)callable).getStatementList(), true);
- assert prevElem != null;
+ PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class);
+ if (function != null) {
+ final PySignature signature = PySignatureCacheManager.getInstance(parameter.getProject()).findSignature(
+ function);
+ String parameterName = parameter.getName();
+ if (signature != null && parameterName != null) {
+ paramType = ObjectUtils.chooseNotNull(signature.getArgTypeQualifiedName(parameterName), paramType);
+ }
+ }
+ return paramType;
+ }
- final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
+
+ static String returnType(@NotNull PyFunction function) {
+ String returnType = PyNames.OBJECT;
+ final PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(
+ function);
+ if (signature != null) {
+ returnType = ObjectUtils.chooseNotNull(signature.getReturnTypeQualifiedName(), returnType);
+ }
+ return returnType;
+ }
+
+ public static PyExpression annotateReturnType(Project project, PyFunction function, boolean createTemplate) {
+ String returnType = returnType(function);
+
+ final String annotationText = " -> " + returnType;
+
+ final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(function.getStatementList(), true);
+ assert prevElem != null;
+
+ final PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
+ Document documentWithCallable = manager.getDocument(function.getContainingFile());
+ if (documentWithCallable != null) {
try {
final TextRange range = prevElem.getTextRange();
- manager.doPostponedOperationsAndUnblockDocument(document);
+ manager.doPostponedOperationsAndUnblockDocument(documentWithCallable);
if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) {
- document.insertString(range.getStartOffset(), annotationText);
+ documentWithCallable.insertString(range.getStartOffset(), annotationText);
}
else {
- document.insertString(range.getEndOffset(), annotationText + ":");
+ documentWithCallable.insertString(range.getEndOffset(), annotationText + ":");
}
}
finally {
- manager.commitDocument(document);
+ manager.commitDocument(documentWithCallable);
}
-
-
- callable = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(callable);
- final PyAnnotation annotation = ((PyFunction)callable).getAnnotation();
- assert annotation != null;
- final PyExpression annotationValue = annotation.getValue();
- assert annotationValue != null : "Generated function must have annotation";
+ }
+
+
+ function = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(function);
+ final PyAnnotation annotation = function.getAnnotation();
+ assert annotation != null;
+ final PyExpression annotationValue = annotation.getValue();
+ assert annotationValue != null : "Generated function must have annotation";
+
+ if (createTemplate) {
final int offset = annotationValue.getTextOffset();
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(annotationValue);
- builder.replaceRange(TextRange.create(0, PyNames.OBJECT.length()), PyNames.OBJECT);
+ builder.replaceRange(TextRange.create(0, returnType.length()), returnType);
final Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
final OpenFileDescriptor descriptor = new OpenFileDescriptor(
project,
- callable.getContainingFile().getVirtualFile(),
+ function.getContainingFile().getVirtualFile(),
offset
);
final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
@@ -149,6 +195,7 @@ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention {
TemplateManager.getInstance(project).startTemplate(targetEditor, template);
}
}
+ return annotationValue;
}
@Override
diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
index effca1d4e9b9..355b2e0f3267 100644
--- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
+++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.codeInsight.intentions;
+import com.google.common.base.Function;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
@@ -80,7 +81,7 @@ public abstract class TypeIntention implements IntentionAction {
}
@Nullable
- protected static PyExpression getProblemElement(@Nullable PsiElement elementAt) {
+ public static PyExpression getProblemElement(@Nullable PsiElement elementAt) {
PyExpression problemElement = PsiTreeUtil.getParentOfType(elementAt, PyNamedParameter.class, PyReferenceExpression.class);
if (problemElement == null) return null;
if (problemElement instanceof PyQualifiedExpression) {
@@ -108,13 +109,22 @@ public abstract class TypeIntention implements IntentionAction {
}
private boolean isAvailableForReturn(@NotNull final PsiElement elementAt) {
+ return resolvesToFunction(elementAt, new Function() {
+ @Override
+ public Boolean apply(PyFunction input) {
+ return !isReturnTypeDefined(input);
+ }
+ });
+ }
+
+ static boolean resolvesToFunction(@NotNull PsiElement elementAt, Function isAvailableForFunction) {
final PyFunction parentFunction = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class);
if (parentFunction != null) {
final ASTNode nameNode = parentFunction.getNameNode();
if (nameNode != null) {
final PsiElement prev = elementAt.getContainingFile().findElementAt(elementAt.getTextOffset()-1);
if (nameNode.getPsi() == elementAt || nameNode.getPsi() == prev) {
- return !isReturnTypeDefined(parentFunction);
+ return isAvailableForFunction.apply(parentFunction);
}
}
}
@@ -126,18 +136,19 @@ public abstract class TypeIntention implements IntentionAction {
final PsiReference reference = callee.getReference();
if (reference instanceof PsiPolyVariantReference) {
final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
- if (results.length == 1) {
- final PsiElement result = results[0].getElement();
- if (!(result instanceof PyFunction)) return false;
- final PsiFile psiFile = result.getContainingFile();
- if (psiFile == null) return false;
- final VirtualFile virtualFile = psiFile.getVirtualFile();
- if (virtualFile != null) {
- if (ProjectRootManager.getInstance(psiFile.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) {
- return false;
+ for (int i = 0; i getParameterType(@NotNull final PyNamedParameter param,
@NotNull final PyFunction func,
@NotNull TypeEvalContext context) {
@@ -34,8 +36,25 @@ public class PyCallSignatureTypeProvider extends PyTypeProviderBase {
if (typeName != null) {
final PyType type = PyTypeParser.getTypeByName(param, typeName);
if (type != null) {
- final PyType evaluatedType = PyDynamicallyEvaluatedType.create(type);
- return Ref.create(evaluatedType);
+ return Ref.create(PyDynamicallyEvaluatedType.create(type));
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Ref getReturnType(@NotNull final PyCallable callable, @NotNull TypeEvalContext context) {
+ if (callable instanceof PyFunction) {
+ PyFunction function = (PyFunction)callable;
+ PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function);
+ if (signature != null) {
+ final String typeName = signature.getReturnType().getTypeQualifiedName();
+ if (typeName != null) {
+ final PyType type = PyTypeParser.getTypeByName(function, typeName);
+ if (type != null) {
+ return Ref.create(PyDynamicallyEvaluatedType.create(type));
+ }
}
}
}
diff --git a/python/src/com/jetbrains/python/debugger/PySignatureCacheManagerImpl.java b/python/src/com/jetbrains/python/debugger/PySignatureCacheManagerImpl.java
index 49ab3ad28202..cbb5bef6f6d0 100644
--- a/python/src/com/jetbrains/python/debugger/PySignatureCacheManagerImpl.java
+++ b/python/src/com/jetbrains/python/debugger/PySignatureCacheManagerImpl.java
@@ -104,7 +104,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
else {
//noinspection ConstantConditions
lines[i] = signatureToString(stringToSignature(file.
- getCanonicalPath(), lines[i]).addAllArgs(signature));
+ getCanonicalPath(), lines[i]).addAllArgs(signature).addReturnType(signature.getReturnTypeQualifiedName()));
}
}
i++;
@@ -140,7 +140,9 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
}
private static String signatureToString(PySignature signature) {
- return signature.getFunctionName() + "\t" + StringUtil.join(arguments(signature), "\t");
+ return signature.getFunctionName() + "\t" + StringUtil.join(arguments(signature), "\t") +
+ (signature.getReturnType() != null
+ ? "\t" + StringUtil.join(signature.getReturnType().getTypesList(), "\t") : "");
}
private static List arguments(PySignature signature) {
@@ -248,8 +250,11 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
if (var.length == 2) {
signature = signature.addArgument(var[0], var[1]);
}
+ else if (var.length == 1) {
+ signature = signature.addReturnType(var[0]);
+ }
else {
- throw new IllegalStateException("Should be : format. " + parts[i] + " instead.");
+ throw new IllegalStateException("Should be : format for arg or for return type; '" + parts[i] + "' instead.");
}
}
return signature;
@@ -259,7 +264,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
@Nullable
private static VirtualFile getFile(@NotNull PySignature signature) {
- return LocalFileSystem.getInstance().findFileByPath(signature.getFile());
+ return LocalFileSystem.getInstance().refreshAndFindFileByPath(signature.getFile());
}
@Nullable
@@ -291,7 +296,7 @@ public class PySignatureCacheManagerImpl extends PySignatureCacheManager {
}
});
}
- }, "Cleaning the cache of dynamically collected types", true, myProject);
+ }, "Cleaning the Cache of Dynamically Collected Types", true, myProject);
String message;
diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/docstrings/PyDocstringGenerator.java
index 5d0d596fb518..50fea52fba76 100644
--- a/python/src/com/jetbrains/python/documentation/docstrings/PyDocstringGenerator.java
+++ b/python/src/com/jetbrains/python/documentation/docstrings/PyDocstringGenerator.java
@@ -274,7 +274,8 @@ public class PyDocstringGenerator {
String type = paramTypes.get(paramCoordinates);
if (type == null && PyCodeInsightSettings.getInstance().INSERT_TYPE_DOCSTUB) {
if (signature != null) {
- type = StringUtil.notNullize(signature.getArgTypeQualifiedName(param.getName()));
+ type = StringUtil.notNullize(param.isReturnValue() ? signature.getReturnTypeQualifiedName() :
+ signature.getArgTypeQualifiedName(param.getName()));
}
else {
type = "";
diff --git a/python/src/com/jetbrains/python/inspections/PyMissingTypeHintsInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingTypeHintsInspection.java
new file mode 100644
index 000000000000..081207b1c7f0
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyMissingTypeHintsInspection.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2015 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.inspections;
+
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.codeInsight.intentions.PyAnnotateTypesIntention;
+import com.jetbrains.python.inspections.quickfix.PyQuickFixUtil;
+import com.jetbrains.python.psi.PyElementVisitor;
+import com.jetbrains.python.psi.PyFunction;
+import com.jetbrains.python.psi.PyNamedParameter;
+import com.jetbrains.python.psi.PyParameter;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author traff
+ */
+public class PyMissingTypeHintsInspection extends PyInspection{
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
+ return new PyElementVisitor() {
+ @Override
+ public void visitPyFunction(PyFunction function) {
+ if (!(function.getTypeComment() != null || typeAnnotationsExist(function))) {
+ ASTNode nameNode = function.getNameNode();
+ if (nameNode != null) {
+ holder.registerProblem(nameNode.getPsi(), "Type hinting is missing for function definition", new AddTypeHintsQuickFix(function.getName()));
+ }
+ }
+ }
+ };
+ }
+
+ private static boolean typeAnnotationsExist(PyFunction function) {
+ for (PyParameter param: function.getParameterList().getParameters()) {
+ PyNamedParameter namedParameter = param.getAsNamed();
+ if (namedParameter != null) {
+ if (namedParameter.getAnnotation() != null) {
+ return true;
+ }
+ }
+ }
+ if (function.getAnnotation() != null) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static class AddTypeHintsQuickFix implements LocalQuickFix {
+ private String myName;
+
+ public AddTypeHintsQuickFix(@NotNull String name) {
+ myName = name;
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getName() {
+ return "Add type hints for '" + myName + "'";
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return "Add type hints";
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ PyFunction function = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyFunction.class);
+
+ if (function != null) {
+ PyAnnotateTypesIntention.annotateTypes(PyQuickFixUtil.getEditor(function), function);
+ }
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
index 9b22b33d8b48..b1016ef85ea8 100644
--- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java
@@ -93,7 +93,7 @@ public class PyProtectedMemberInspection extends PyInspection {
private boolean isImportFromTheSamePackage(PyReferenceExpression importSource) {
PsiDirectory directory = importSource.getContainingFile().getContainingDirectory();
- if (PyUtil.isPackage(directory, true, importSource.getContainingFile()) &&
+ if (directory != null && PyUtil.isPackage(directory, true, importSource.getContainingFile()) &&
directory.getName().equals(importSource.getName())) {
return true;
}
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/DocstringQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/DocstringQuickFix.java
index 0622727ebc1b..a42ddfdc7cca 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/DocstringQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/DocstringQuickFix.java
@@ -17,12 +17,7 @@ package com.jetbrains.python.inspections.quickfix;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiDocumentManager;
-import com.intellij.psi.PsiElement;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.util.PsiTreeUtil;
@@ -73,20 +68,6 @@ public class DocstringQuickFix implements LocalQuickFix {
return "Fix docstring";
}
- @Nullable
- private static Editor getEditor(@NotNull PsiElement element) {
- Document document = PsiDocumentManager.getInstance(element.getProject()).getDocument(element.getContainingFile());
- if (document != null) {
- final EditorFactory instance = EditorFactory.getInstance();
- if (instance == null) return null;
- Editor[] editors = instance.getEditors(document);
- if (editors.length > 0) {
- return editors[0];
- }
- }
- return null;
- }
-
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PyDocStringOwner.class);
if (docStringOwner == null) return;
@@ -113,7 +94,7 @@ public class DocstringQuickFix implements LocalQuickFix {
private static void addEmptyDocstring(@NotNull PyDocStringOwner docStringOwner) {
if (docStringOwner instanceof PyFunction ||
docStringOwner instanceof PyClass && ((PyClass)docStringOwner).findInitOrNew(false, null) != null) {
- PyGenerateDocstringIntention.generateDocstring(docStringOwner, getEditor(docStringOwner));
+ PyGenerateDocstringIntention.generateDocstring(docStringOwner, PyQuickFixUtil.getEditor(docStringOwner));
}
}
}
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyImplementMethodsQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyImplementMethodsQuickFix.java
index 282bca97dbb5..d480f4e203e3 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/PyImplementMethodsQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/PyImplementMethodsQuickFix.java
@@ -17,11 +17,8 @@ package com.jetbrains.python.inspections.quickfix;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.PyBundle;
@@ -31,7 +28,6 @@ import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Set;
@@ -62,7 +58,7 @@ public class PyImplementMethodsQuickFix extends LocalQuickFixOnPsiElement {
@Override
public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
- final Editor editor = getEditor(project, file);
+ final Editor editor = PyQuickFixUtil.getEditor(file);
if (editor != null && startElement instanceof PyClass) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
@@ -81,18 +77,4 @@ public class PyImplementMethodsQuickFix extends LocalQuickFixOnPsiElement {
}
}
}
-
- @Nullable
- private static Editor getEditor(Project project, PsiFile file) {
- Document document = PsiDocumentManager.getInstance(project).getDocument(file);
- if (document != null) {
- final EditorFactory instance = EditorFactory.getInstance();
- if (instance == null) return null;
- Editor[] editors = instance.getEditors(document);
- if (editors.length > 0) {
- return editors[0];
- }
- }
- return null;
- }
}
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyQuickFixUtil.java b/python/src/com/jetbrains/python/inspections/quickfix/PyQuickFixUtil.java
new file mode 100644
index 000000000000..3ad435445642
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/quickfix/PyQuickFixUtil.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2016 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.inspections.quickfix;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author traff
+ */
+public class PyQuickFixUtil {
+ @Nullable
+ public static Editor getEditor(@NotNull PsiElement element) {
+ Document document = PsiDocumentManager.getInstance(element.getProject()).getDocument(element.getContainingFile());
+ if (document != null) {
+ final EditorFactory instance = EditorFactory.getInstance();
+ if (instance == null) return null;
+ Editor[] editors = instance.getEditors(document);
+ if (editors.length > 0) {
+ return editors[0];
+ }
+ }
+ return null;
+ }
+}
diff --git a/python/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java b/python/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java
index 4dff5a00d886..5e961060c1a1 100644
--- a/python/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java
+++ b/python/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java
@@ -15,7 +15,6 @@
*/
package com.jetbrains.python.psi.types;
-import com.intellij.openapi.project.Project;
import com.jetbrains.python.PyNames;
import org.jetbrains.annotations.NotNull;
diff --git a/python/testData/dynamicTypes/1.py b/python/testData/dynamicTypes/1.py
new file mode 100644
index 000000000000..e7c846b2c640
--- /dev/null
+++ b/python/testData/dynamicTypes/1.py
@@ -0,0 +1,4 @@
+def foo(x):
+ return x
+
+foo(1)
\ No newline at end of file
diff --git a/python/testData/dynamicTypes/1_after.py b/python/testData/dynamicTypes/1_after.py
new file mode 100644
index 000000000000..e647aea92013
--- /dev/null
+++ b/python/testData/dynamicTypes/1_after.py
@@ -0,0 +1,11 @@
+def foo(x):
+ """
+
+ :param x:
+ :type x: int
+ :return:
+ :rtype: int
+ """
+ return x
+
+foo(1)
\ No newline at end of file
diff --git a/python/testData/inspections/PyMissingTypeHintsInspection/noAnnotations.py b/python/testData/inspections/PyMissingTypeHintsInspection/noAnnotations.py
new file mode 100644
index 000000000000..73bdd73dbc82
--- /dev/null
+++ b/python/testData/inspections/PyMissingTypeHintsInspection/noAnnotations.py
@@ -0,0 +1,2 @@
+def foo (x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyMissingTypeHintsInspection/py3kAnnotations.py b/python/testData/inspections/PyMissingTypeHintsInspection/py3kAnnotations.py
new file mode 100644
index 000000000000..caefec0ac1f7
--- /dev/null
+++ b/python/testData/inspections/PyMissingTypeHintsInspection/py3kAnnotations.py
@@ -0,0 +1,2 @@
+def foo(x: int, y: int) -> str:
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyMissingTypeHintsInspection/typeComment.py b/python/testData/inspections/PyMissingTypeHintsInspection/typeComment.py
new file mode 100644
index 000000000000..38efd7e111c2
--- /dev/null
+++ b/python/testData/inspections/PyMissingTypeHintsInspection/typeComment.py
@@ -0,0 +1,3 @@
+def foo(x):
+ # type: (int) -> int
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyMissingTypeHintsInspection/typeCommentOnTheSameLine.py b/python/testData/inspections/PyMissingTypeHintsInspection/typeCommentOnTheSameLine.py
new file mode 100644
index 000000000000..2b42a1884977
--- /dev/null
+++ b/python/testData/inspections/PyMissingTypeHintsInspection/typeCommentOnTheSameLine.py
@@ -0,0 +1,2 @@
+def foo(x): # type: (int) -> int
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition.py
new file mode 100644
index 000000000000..cb92a3843add
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py
new file mode 100644
index 000000000000..a313e5f14fb9
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnDefinition_after.py
@@ -0,0 +1,2 @@
+def foo(x: object, y: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py
new file mode 100644
index 000000000000..39e5e0eeeb14
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py
new file mode 100644
index 000000000000..26ce2df47d40
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnImportedInvocation_after.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py
new file mode 100644
index 000000000000..6c5aa528d332
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation.py
@@ -0,0 +1,5 @@
+def foo(x, y):
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py
new file mode 100644
index 000000000000..38197b897a1d
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/caretOnInvocation_after.py
@@ -0,0 +1,5 @@
+def foo(x: object, y: object) -> object:
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py
new file mode 100644
index 000000000000..282d108e2e52
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py
new file mode 100644
index 000000000000..a313e5f14fb9
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/foo_decl_after.py
@@ -0,0 +1,2 @@
+def foo(x: object, y: object) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment.py
new file mode 100644
index 000000000000..cb92a3843add
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment_after.py b/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment_after.py
new file mode 100644
index 000000000000..cb635649431a
--- /dev/null
+++ b/python/testData/intentions/PyAnnotateTypesIntentionTest/typeComment_after.py
@@ -0,0 +1,3 @@
+def foo(x, y):
+ # type: (object, object) -> object
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py
new file mode 100644
index 000000000000..cb92a3843add
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py
new file mode 100644
index 000000000000..50cfaf949af5
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnDefinition_after.py
@@ -0,0 +1,2 @@
+def foo(x, y) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py
new file mode 100644
index 000000000000..39e5e0eeeb14
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py
new file mode 100644
index 000000000000..26ce2df47d40
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnImportedInvocation_after.py
@@ -0,0 +1,3 @@
+from foo_decl import foo
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py
new file mode 100644
index 000000000000..6c5aa528d332
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation.py
@@ -0,0 +1,5 @@
+def foo(x, y):
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py
new file mode 100644
index 000000000000..5b42ec7130fc
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnInvocation_after.py
@@ -0,0 +1,5 @@
+def foo(x, y) -> object:
+ pass
+
+
+foo(1, 1)
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py
new file mode 100644
index 000000000000..16d57c86b703
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage.py
@@ -0,0 +1,3 @@
+def foo(var):
+ print(var)
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py
new file mode 100644
index 000000000000..421e1015965a
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/caretOnParamUsage_after.py
@@ -0,0 +1,3 @@
+def foo(var: object):
+ print(var)
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py
new file mode 100644
index 000000000000..282d108e2e52
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl.py
@@ -0,0 +1,2 @@
+def foo(x, y):
+ pass
\ No newline at end of file
diff --git a/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py
new file mode 100644
index 000000000000..50cfaf949af5
--- /dev/null
+++ b/python/testData/intentions/SpecifyTypeInPy3AnnotationsIntentionTest/foo_decl_after.py
@@ -0,0 +1,2 @@
+def foo(x, y) -> object:
+ pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java
index 8c7ffec9a571..81239701f2d5 100644
--- a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java
+++ b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java
@@ -107,7 +107,7 @@ public class PyEnvTaskRunner {
if (url == null) {
return null;
}
- return PyTestSdkTools.createTempSdk(url, SdkCreationType.SDK_PACKAGES_AND_SKELETONS, null);
+ return PyTestSdkTools.createTempSdk(url, SdkCreationType.EMPTY_SDK, null);
}
protected boolean shouldRun(String root, PyTestTask task) {
diff --git a/python/testSrc/com/jetbrains/env/PyTestTask.java b/python/testSrc/com/jetbrains/env/PyTestTask.java
index 64c76c9b888f..6175290db617 100644
--- a/python/testSrc/com/jetbrains/env/PyTestTask.java
+++ b/python/testSrc/com/jetbrains/env/PyTestTask.java
@@ -50,6 +50,9 @@ public abstract class PyTestTask {
public void after() throws Exception {
}
+ public void doFinally() {
+ }
+
public void useNormalTimeout() {
}
diff --git a/python/testSrc/com/jetbrains/env/python/PyDynamicTypesTest.java b/python/testSrc/com/jetbrains/env/python/PyDynamicTypesTest.java
new file mode 100644
index 000000000000..d39f45ad0571
--- /dev/null
+++ b/python/testSrc/com/jetbrains/env/python/PyDynamicTypesTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2016 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.env.python;
+
+import com.google.common.collect.Lists;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.EditorTestUtil;
+import com.jetbrains.env.PyEnvTestCase;
+import com.jetbrains.env.python.debug.PyDebuggerTask;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.debugger.PyDebuggerOptionsProvider;
+import com.jetbrains.python.debugger.PySignatureCacheManagerImpl;
+
+import java.io.IOException;
+
+/**
+ * @author traff
+ */
+
+public class PyDynamicTypesTest extends PyEnvTestCase {
+ public void test1() throws Exception {
+ doTest(getTestName(true) + ".py");
+ }
+
+ private void doTest(final String scriptName) {
+ runPythonTest(new PyDebuggerTask("/" + "dynamicTypes", scriptName) {
+ @Override
+ public void setUp(String testName) throws Exception {
+ super.setUp(testName);
+ }
+
+ @Override
+ public void before() throws Exception {
+ PyDebuggerOptionsProvider.getInstance(myFixture.getProject()).setSaveCallSignatures(true);
+ }
+
+ public void doFinally() {
+ try {
+ PySignatureCacheManagerImpl.CALL_SIGNATURES_ATTRIBUTE.writeAttributeBytes(getVirtualFile(), "".getBytes());
+ }
+ catch (IOException e) {
+ //pass
+ }
+ PyDebuggerOptionsProvider.getInstance(myFixture.getProject()).setSaveCallSignatures(false);
+ }
+
+ private VirtualFile getVirtualFile() {
+ return LocalFileSystem.getInstance()
+ .refreshAndFindFileByPath(
+ getTestDataPath() +
+ "/dynamicTypes/" +
+ scriptName);
+ }
+
+ @Override
+ public void testing() throws Exception {
+ waitForTerminate();
+
+ edt(() -> {
+ myFixture.configureByFile("dynamicTypes/" + scriptName);
+
+ try {
+ //copy signature attributes from real file to temporary test file
+ byte[] bytes = PySignatureCacheManagerImpl.CALL_SIGNATURES_ATTRIBUTE
+ .readAttributeBytes(getVirtualFile());
+ PySignatureCacheManagerImpl.CALL_SIGNATURES_ATTRIBUTE.writeAttributeBytes(myFixture.getFile().getVirtualFile(),
+ bytes);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ EditorTestUtil.setCaretsAndSelection(myFixture.getEditor(), new EditorTestUtil.CaretAndSelectionState(
+ Lists.newArrayList(new EditorTestUtil.CaretInfo(new LogicalPosition(0, 6), null)), null));
+ final IntentionAction action = myFixture.findSingleIntention(PyBundle.message("INTN.doc.string.stub"));
+ myFixture.launchAction(action);
+ myFixture.checkResultByFile("dynamicTypes/" + getTestName(true) + "_after.py");
+ });
+ }
+ });
+ }
+}
+
diff --git a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java
index d0a715a9d729..d7fb13129d0b 100644
--- a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java
+++ b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java
@@ -441,7 +441,7 @@ public class PythonDebuggerTest extends PyEnvTestCase {
});
}
- private static void createExceptionBreak(IdeaProjectTestFixture fixture,
+ public static void createExceptionBreak(IdeaProjectTestFixture fixture,
boolean notifyOnTerminate,
boolean notifyOnFirst,
boolean ignoreLibraries) {
diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java
index 881e366306f1..6c6f05240de3 100644
--- a/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java
+++ b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java
@@ -316,6 +316,8 @@ public abstract class PyBaseDebuggerTask extends PyExecutionFixtureTestTask {
throw new RuntimeException(output(), e);
}
finally {
+ doFinally();
+
clearAllBreakpoints();
setProcessCanTerminate(true);
diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java
index 7f61cded1351..b6069929e225 100644
--- a/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java
+++ b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java
@@ -29,6 +29,7 @@ import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.xdebugger.*;
+import com.jetbrains.env.python.PythonDebuggerTest;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebugRunner;
import com.jetbrains.python.run.PythonCommandLineState;
@@ -117,6 +118,8 @@ public class PyDebuggerTask extends PyBaseDebuggerTask {
final int serverLocalPort = serverSocket.getLocalPort();
final RunProfile profile = env.getRunProfile();
+ PythonDebuggerTest.createExceptionBreak(myFixture, false, false, false); //turn off exception breakpoints by default
+
before();
setProcessCanTerminate(false);
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyMissingTypeHintsInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyMissingTypeHintsInspectionTest.java
new file mode 100644
index 000000000000..fd6ca83100bc
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/inspections/PyMissingTypeHintsInspectionTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2016 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.inspections;
+
+import com.jetbrains.python.fixtures.PyTestCase;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
+
+/**
+ * @author traff
+ */
+public class PyMissingTypeHintsInspectionTest extends PyTestCase {
+ public void testPy3kAnnotations() {
+ doTest(LanguageLevel.PYTHON35);
+ }
+
+ public void testNoAnnotations() {
+ doTest(LanguageLevel.PYTHON35);
+ }
+
+ public void testTypeComment() {
+ doTest(LanguageLevel.PYTHON27);
+ }
+
+ public void testTypeCommentOnTheSameLine() {
+ doTest(LanguageLevel.PYTHON27);
+ }
+
+ private void doTest(LanguageLevel languageLevel) {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel);
+ try {
+ myFixture.configureByFile("inspections/PyMissingTypeHintsInspection/" + getTestName(true) + ".py");
+ myFixture.enableInspections(PyMissingTypeHintsInspection.class);
+ myFixture.checkHighlighting(false, false, true);
+ }
+ finally {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
+ }
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
new file mode 100644
index 000000000000..77cd98c1da43
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/intentions/PyAnnotateTypesIntentionTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2016 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.intentions;
+
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
+
+/**
+ * @author traff
+ */
+public class PyAnnotateTypesIntentionTest extends PyIntentionTestCase {
+ public void testCaretOnDefinition() {
+ doTest();
+ }
+
+ public void testCaretOnInvocation() {
+ doTest();
+ }
+
+ public void testCaretOnImportedInvocation() {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30);
+ try {
+ doIntentionTest(PyBundle.message("INTN.annotate.types"), getTestName(true) + ".py", "foo_decl.py");
+ myFixture.checkResultByFile("foo_decl.py", "foo_decl_after.py", false);
+ }
+ finally {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
+ }
+ }
+
+ public void testTypeComment() {
+ doTest(PyBundle.message("INTN.annotate.types"), LanguageLevel.PYTHON27);
+ }
+
+
+ private void doTest() {
+ doTest(PyBundle.message("INTN.annotate.types"), LanguageLevel.PYTHON30);
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
index 0d2bee5822c6..cd23d8b7c9be 100644
--- a/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
+++ b/python/testSrc/com/jetbrains/python/intentions/PyIntentionTestCase.java
@@ -42,9 +42,13 @@ public abstract class PyIntentionTestCase extends PyTestCase {
}
}
- protected void doIntentionTest(final String hint) {
+ protected void doIntentionTest(final String hint, String ... files) {
final String testFileName = getTestName(true);
- myFixture.configureByFile(testFileName + ".py");
+ if (files.length>0) {
+ myFixture.configureByFiles(files);
+ } else {
+ myFixture.configureByFile(testFileName + ".py");
+ }
final IntentionAction intentionAction = myFixture.findSingleIntention(hint);
assertNotNull(intentionAction);
myFixture.launchAction(intentionAction);
diff --git a/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java
new file mode 100644
index 000000000000..01cc3d2f9c6e
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/intentions/SpecifyTypeInPy3AnnotationsIntentionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2016 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.intentions;
+
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
+
+/**
+ * @author traff
+ */
+public class SpecifyTypeInPy3AnnotationsIntentionTest extends PyIntentionTestCase {
+ public void testCaretOnDefinition() {
+ doTestReturnType();
+ }
+
+
+
+ public void testCaretOnInvocation() {
+ doTestReturnType();
+ }
+
+ public void testCaretOnImportedInvocation() {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30);
+ try {
+ doIntentionTest(PyBundle.message("INTN.specify.return.type.in.annotation"), getTestName(true) + ".py", "foo_decl.py");
+ myFixture.checkResultByFile("foo_decl.py", "foo_decl_after.py", false);
+ }
+ finally {
+ PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
+ }
+ }
+
+ public void testCaretOnParamUsage() {
+ doTestParam();
+ }
+
+
+ private void doTestReturnType() {
+ doTest(PyBundle.message("INTN.specify.return.type.in.annotation"), LanguageLevel.PYTHON30);
+ }
+
+
+ private void doTestParam() {
+ doTest(PyBundle.message("INTN.specify.type.in.annotation"), LanguageLevel.PYTHON30);
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java
index 3a010450382d..ff3d241454c1 100644
--- a/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java
+++ b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java
@@ -62,7 +62,11 @@ public final class PyTestSdkTools {
@Override
public void run() {
- final Sdk sdk = SdkConfigurationUtil.setupSdk(NO_SDK, sdkHome, PythonSdkType.getInstance(), true, null, null);
+ final Sdk sdk = SdkConfigurationUtil.setupSdk(NO_SDK, sdkHome, new PythonSdkType() {
+ @Override
+ public void setupSdkPaths(@NotNull Sdk sdk) {
+ }
+ }, true, null, null);
Assert.assertNotNull("Failed to create SDK on " + sdkHome, sdk);
ref.set(sdk);
}