Merge remote-tracking branch 'origin/master'

This commit is contained in:
Alexander Koshevoy
2016-02-25 15:26:39 +03:00
85 changed files with 1480 additions and 457 deletions
@@ -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())
@@ -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)
@@ -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
@@ -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 = ["<xml>"]
@@ -116,14 +151,40 @@ def create_signature_message(signature):
for arg in signature.args:
cmdTextList.append('<arg name="%s" type="%s"></arg>' % (pydevd_vars.make_valid_xml_value(arg[0]), pydevd_vars.make_valid_xml_value(arg[1])))
if signature.return_type is not None:
cmdTextList.append('<return type="%s"></return>' % (pydevd_vars.make_valid_xml_value(signature.return_type)))
cmdTextList.append("</call_signature></xml>")
cmdText = ''.join(cmdTextList)
return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText)
def send_signature_call_trace(dbg, frame, filename):
if dbg.signature_factory.is_in_scope(filename):
dbg.writer.add_command(create_signature_message(dbg.signature_factory.create_signature(frame)))
if dbg.signature_factory and dbg.signature_factory.is_in_scope(filename):
signature = dbg.signature_factory.create_signature(frame)
if dbg.signature_factory.cache is not None:
if not dbg.signature_factory.cache.is_in_cache(signature):
dbg.signature_factory.cache.add(signature)
dbg.writer.add_command(create_signature_message(signature))
return True
else:
# we don't send signature if it is cached
return False
else:
dbg.writer.add_command(create_signature_message(signature))
return True
return False
def send_signature_return_trace(dbg, frame, filename, return_value):
if dbg.signature_factory and dbg.signature_factory.is_in_scope(filename):
signature = dbg.signature_factory.create_signature(frame, with_args=False)
signature.return_type = get_type_of_value(return_value)
dbg.writer.add_command(create_signature_message(signature))
return True
return False
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -52,6 +52,10 @@
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbCodeCellAction" id="IpnbCodeCellAction" text="Code Cell">
<keyboard-shortcut keymap="$default" first-keystroke="ctrl shift s"/>
</action>
<action id="NewIPynbFile" class="org.jetbrains.plugins.ipnb.editor.actions.IpnbCreateFileAction">
<add-to-group group-id="NewGroup" anchor="after" relative-to-action="NewPythonFile"/>
</action>
</actions>
</idea-plugin>
@@ -0,0 +1,34 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
@@ -1,4 +0,0 @@
## This is hand-edited. Add all of the non-auto-generated definitions
3A6:varPhi:ALPHA
3C6:varphi:ALPHA
02248:thickapprox:REL
@@ -0,0 +1,28 @@
body {
background-color: #313335 !important;
color: #bababa !important;
}
.MathJax_Display {
color: #bababa !important;
background-color: #313335 !important;
}
#mydiv {
color: #bababa !important;
background-color: #313335 !important;
}
.MathJax {
background-color: #313335 !important;
color: #bababa !important;
}
a {
color: #589df6 !important;
}
.MathJax_Preview {
color: #bababa !important;
}
@@ -1,243 +1,271 @@
package org.jetbrains.plugins.ipnb;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.ui.LafManager;
import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.MarkdownUtil;
import com.intellij.util.ui.UIUtil;
import com.petebevin.markdown.MarkdownProcessor;
import net.sourceforge.jeuclid.MathMLParserSupport;
import net.sourceforge.jeuclid.context.LayoutContextImpl;
import net.sourceforge.jeuclid.context.Parameter;
import net.sourceforge.jeuclid.converter.Converter;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import netscape.javascript.JSException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbTexPackageDefinitions;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import uk.ac.ed.ph.snuggletex.SnuggleEngine;
import uk.ac.ed.ph.snuggletex.SnuggleInput;
import uk.ac.ed.ph.snuggletex.SnuggleSession;
import uk.ac.ed.ph.snuggletex.XMLStringOutputOptions;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import javax.swing.*;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import javax.xml.parsers.ParserConfigurationException;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.awt.event.MouseWheelEvent;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class IpnbUtils {
private static final Logger LOG = Logger.getInstance(IpnbUtils.class);
private static final String ourPrefix = "<html><head><script type=\"text/x-mathjax-config\">\n" +
" MathJax.Hub.Config({\n" +
" tex2jax: {\n" +
" inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ],\n" +
" displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ],\n" +
" processEscapes: true,\n" +
" processEnvironments: true\n" +
" },\n" +
" displayAlign: 'center',\n" +
" \"HTML-CSS\": {\n" +
" styles: {'#mydiv': {\"font-size\": %s}},\n" +
" preferredFont: null,\n" +
" linebreaks: { automatic: true }\n" +
" }\n" +
" });\n" +
"</script><script type=\"text/javascript\"\n" +
" src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full\">\n" +
" </script></head><body><div id=\"mydiv\">";
private static final String ourPostfix = "</div></body></html>";
private static final MarkdownProcessor ourMarkdownProcessor = new MarkdownProcessor();
private static final String ourImagePrefix = "http://image";
private static final Font ourFont = new Font(Font.SERIF, Font.PLAIN, 16);
private static final String ourBodyRule = "body { font-family: \"DejaVu\"; " +
"font-size: " + ourFont.getSize() + "pt;}";
private static URL ourStyleUrl;
private static final String ourCodeRule = "code { font-family: \"DejaVu\"; " +
"font-size: " + ourFont.getSize() + "pt;}";
public static JComponent createLatexPane(@NotNull final String source, int width) {
final JComponent panel = createHtmlPanel(source, width);
private static final String ourAlertRule = ".alert{ background-color: #fcf8e3;" +
"color: #c09853;" +
"padding: 8px 35px 8px 14px;" +
"border: 1px solid #fbeed5;}";
private static final String ourAlertSuccessRule = ".alert-success{ background-color: #dff0d8;\n" +
"color: #468847;" +
"padding: 8px 35px 8px 14px;" +
"border: 1px solid #d6e9c6;}";
private static final String ourAlertErrorRule = ".alert-error{ background-color: #f2dede;\n" +
"color: #b94a48;" +
"padding: 8px 35px 8px 14px;" +
"border: 1px solid #eed3d7;}";
public static String markdown2Html(@NotNull String description) {
description = StringUtil.replace(description, "class=\"alert alert-success\"", "class=\"alert-success\"");
description = StringUtil.replace(description, "class=\"alert alert-error\"", "class=\"alert-error\"");
ArrayList<String> lines = ContainerUtil.newArrayList(description.split("\n|\r|\r\n"));
MarkdownUtil.replaceHeaders(lines);
return ourMarkdownProcessor.markdown(StringUtil.join(lines, "\n"));
}
public static JEditorPane createLatexPane(@NotNull final String source) {
final JEditorPane editorPane = new JEditorPane();
editorPane.setBackground(IpnbEditorUtil.getBackground());
editorPane.setContentType(new HTMLEditorKit().getContentType());
editorPane.setEditorKit(new HTMLEditorKit());
final HTMLDocument document = (HTMLDocument)editorPane.getDocument();
final StyleSheet sheet = document.getStyleSheet();
sheet.addRule(ourBodyRule);
sheet.addRule(ourCodeRule);
sheet.addRule(ourAlertRule);
sheet.addRule(ourAlertSuccessRule);
sheet.addRule(ourAlertErrorRule);
editorPane.setEditable(false);
final String html = convertToHtml(source, document);
editorPane.setText("<html><body>" + html + "</body></html>");
editorPane.addMouseListener(new MouseAdapter() {
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
final Container parent = editorPane.getParent();
final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(editorPane, e, parent);
final Container parent = panel.getParent();
final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(panel, e, parent);
parent.dispatchEvent(parentEvent);
}
});
editorPane.addHyperlinkListener(new BrowserHyperlinkListener());
//TODO: jump to the section (see User Interface#Utilities)
return editorPane;
return panel;
}
public static JComponent createHtmlPanel(@NotNull final String source, int width) {
Platform.setImplicitExit(false);
private static String convertToHtml(@NotNull final String source, @NotNull final HTMLDocument document) {
final JFXPanel javafxPanel = new JFXPanel(){
@Override
protected void processMouseWheelEvent(MouseWheelEvent e) {
final Container parent = getParent();
final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(this, e, parent);
parent.dispatchEvent(parentEvent);
}
};
javafxPanel.setBackground(IpnbEditorUtil.getBackground());
Platform.runLater(() -> {
final WebView webView = new WebView();
webView.setOnDragDetected(new EventHandler<javafx.scene.input.MouseEvent>() {
@Override
public void handle(javafx.scene.input.MouseEvent event) {
}
});
final WebEngine engine = webView.getEngine();
initHyperlinkListener(engine);
engine.setOnStatusChanged(new EventHandler<WebEvent<String>>() {
public void handle(WebEvent<String> status) {
adjustHeight(webView, javafxPanel, source);
}
});
final String prefix = String.format(ourPrefix, EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
engine.loadContent(prefix + convertToHtml(source) + ourPostfix);
final BorderPane pane = new BorderPane(webView);
final Scene scene = new Scene(pane, width != 0 ? width : 20, 20);
javafxPanel.setScene(scene);
Platform.runLater(() -> adjustHeight(webView, javafxPanel, source));
updateLaf(LafManager.getInstance().getCurrentLookAndFeel() instanceof DarculaLookAndFeelInfo,
pane, engine, javafxPanel);
});
return javafxPanel;
}
private static String convertToHtml(@NotNull String source) {
source = StringUtil.replace(source, "class=\"alert alert-success\"", "class=\"alert-success\"");
source = StringUtil.replace(source, "class=\"alert alert-error\"", "class=\"alert-error\"");
ArrayList<String> lines = ContainerUtil.newArrayList(source.split("\n|\r|\r\n"));
MarkdownUtil.replaceHeaders(lines);
source = StringUtil.join(lines, "\n");
final StringBuilder result = new StringBuilder();
StringBuilder markdown = new StringBuilder();
StringBuilder formula = new StringBuilder();
boolean inCode = false;
int inMultiStringCode = 0;
boolean inEnd = false;
source = replaceLinks(source);
source = ourMarkdownProcessor.markdown(source);
boolean escaped = false;
boolean backQuoted = false;
int imageIndex = 0;
for (int i = 0; i != source.length(); ++i) {
final char charAt = source.charAt(i);
if (charAt == '`') {
backQuoted = !backQuoted;
if (source.length() > i + 2 && source.charAt(i + 1) == '`' && source.charAt(i + 2) == '`') {
markdown.append(escaped ? "</code>" : "<code>");
escaped = !escaped;
//noinspection AssignmentToForLoopParameter
i += 2;
continue;
}
}
if (escaped && charAt == '\n') {
markdown.append("<br/>");
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? "</pre>" : "<pre>");
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("<a href=\"").append(url).append("\"");
if(title != null) {
link.append(" title=\"");
link.append(title);
link.append("\"");
}
final SnuggleSession session = engine.createSession();
link.append(">").append(linkText);
link.append("</a>");
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<URL, BufferedImage> cache = (Dictionary<URL, BufferedImage>)editorDocument.getProperty("imageCache");
if (cache == null) {
//noinspection UseOfObsoleteCollectionType
cache = new Hashtable<URL, BufferedImage>();
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("<p style = \"text-align:center;\"><img src=\"").append(ourImagePrefix).append(imageIndex).append(".jpg\"/></p>");
}
else
result.append("<img src=\"").append(ourImagePrefix).append(imageIndex).append(".jpg\"/>");
}
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();
});
}
}
@@ -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) {
@@ -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;
}
}
@@ -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);
@@ -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<JBLabel, IpnbHeadingCell
}
private String renderCellText() {
return "<html><body><h" + myCell.getLevel() + ">" + myCell.getSourceAsString() + "</h" + myCell.getLevel() +
return "<html><body><h" + myCell.getLevel() + " style=\"font-size: " + EditorColorsManager
.getInstance().getGlobalScheme().getEditorFontSize() + ";\">" + myCell.getSourceAsString() + "</h" + myCell.getLevel() +
"></body></html>";
}
@@ -6,10 +6,13 @@ import org.jetbrains.plugins.ipnb.format.cells.IpnbMarkdownCell;
import javax.swing.*;
public class IpnbMarkdownPanel extends IpnbEditablePanel<JEditorPane, IpnbMarkdownCell> {
public class IpnbMarkdownPanel extends IpnbEditablePanel<JComponent, IpnbMarkdownCell> {
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<JEditorPane, IpnbMarkdo
}
@Override
protected JEditorPane createViewPanel() {
return IpnbUtils.createLatexPane(myCell.getSourceAsString());
protected JComponent createViewPanel() {
int width = myParent.getWidth();
return IpnbUtils.createLatexPane(myCell.getSourceAsString(), width);
}
@Override
@@ -32,6 +36,6 @@ public class IpnbMarkdownPanel extends IpnbEditablePanel<JEditorPane, IpnbMarkdo
@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
protected Object clone() {
return new IpnbMarkdownPanel((IpnbMarkdownCell)myCell.clone());
return new IpnbMarkdownPanel((IpnbMarkdownCell)myCell.clone(), myParent);
}
}
@@ -1,55 +0,0 @@
package org.jetbrains.plugins.ipnb.editor.panels;
import org.w3c.dom.Element;
import uk.ac.ed.ph.snuggletex.SnugglePackage;
import uk.ac.ed.ph.snuggletex.dombuilding.CommandHandler;
import uk.ac.ed.ph.snuggletex.dombuilding.EqnArrayHandler;
import uk.ac.ed.ph.snuggletex.internal.DOMBuilder;
import uk.ac.ed.ph.snuggletex.internal.SnuggleParseException;
import uk.ac.ed.ph.snuggletex.semantics.Interpretation;
import uk.ac.ed.ph.snuggletex.semantics.MathOperatorInterpretation;
import uk.ac.ed.ph.snuggletex.tokens.CommandToken;
import uk.ac.ed.ph.snuggletex.tokens.FlowToken;
import java.util.List;
import static uk.ac.ed.ph.snuggletex.definitions.Globals.ALL_MODES;
import static uk.ac.ed.ph.snuggletex.definitions.Globals.MATH_MODE_ONLY;
import static uk.ac.ed.ph.snuggletex.definitions.LaTeXMode.MATH;
import static uk.ac.ed.ph.snuggletex.definitions.TextFlowContext.START_NEW_XHTML_BLOCK;
public final class IpnbTexPackageDefinitions {
private static final SnugglePackage ourPackage;
public static SnugglePackage getPackage() {
return ourPackage;
}
private static String ourMathCharacters = "org/jetbrains/plugins/ipnb/math-characters.txt";
static {
ourPackage = new SnugglePackage("Ipnb");
ourPackage.addComplexCommandSameArgMode("dot", false, 1, MATH_MODE_ONLY, new CommandHandler() {
@Override
public void handleCommand(DOMBuilder builder, Element parentElement, CommandToken token) throws SnuggleParseException {
List<FlowToken> 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);
}
}
@@ -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<K extends IpnbOutputCell> extends IpnbPanel<JComponent, K> {
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<K extends IpnbOutputCell> extends IpnbPanel<JCo
JTextArea textArea = new JTextArea(myCell.getSourceAsString());
textArea.setLineWrap(true);
textArea.setEditable(false);
final Font font = textArea.getFont();
final Font newFont = new Font(font.getName(), font.getStyle(), EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
textArea.setFont(newFont);
textArea.setBackground(IpnbEditorUtil.getBackground());
return textArea;
}
@@ -85,11 +85,11 @@ public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
}
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<JComponent, IpnbCodeCell> {
}
else if (outputCell.getSourceAsString() != null) {
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
new IpnbCodeOutputPanel<IpnbOutputCell>(outputCell));
new IpnbCodeOutputPanel<IpnbOutputCell>(outputCell, myParent.getIpnbFilePanel()));
}
mainPanel.add(panel);
}
@@ -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<IpnbErrorOutputCell> {
public IpnbErrorPanel(@NotNull final IpnbErrorOutputCell cell) {
super(cell);
super(cell, null);
}
@Override
@@ -24,6 +25,9 @@ public class IpnbErrorPanel extends IpnbCodeOutputPanel<IpnbErrorOutputCell> {
final List<String> 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);
@@ -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<IpnbHtmlOutputCell> {
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("<html>");
for (String html : myCell.getHtmls()) {
html = html.replace("\"", "'");
text.append(html);
}
text.append("</html>");
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);
}
}
@@ -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<IpnbImageOutputCell> {
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<IpnbImageOutputCell> {
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<IpnbImageOutputCell> {
return label;
}
}
}
@@ -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<IpnbLatexOutputCell> {
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);
}
}
@@ -8,7 +8,7 @@ import javax.swing.*;
public class IpnbStreamPanel extends IpnbCodeOutputPanel<IpnbStreamOutputCell> {
public IpnbStreamPanel(@NotNull final IpnbStreamOutputCell cell) {
super(cell);
super(cell, null);
}
@Override
@@ -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<NamedParameter> 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;
@@ -30,6 +30,7 @@ public abstract class AbstractCommand<T> {
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<T> {
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<T> {
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;
@@ -31,10 +31,15 @@ public class ProtocolParser {
while (reader.hasMoreChildren()) {
reader.moveDown();
if (!"arg".equals(reader.getNodeName())) {
throw new PyDebuggerException("Expected <arg>, 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 <arg> or <return>, found " + reader.getNodeName());
}
signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
reader.moveUp();
}
@@ -0,0 +1,7 @@
<html>
<body>
<span style="font-family: verdana,serif;">
This inspection detects lack of type hints for function declaration in
one of the two formats: parameter annotations or a type comment
</body>
</html>
@@ -0,0 +1,2 @@
def foo(var: object) -> object:
pass
@@ -0,0 +1,2 @@
def foo(var):
pass
@@ -0,0 +1,9 @@
<html>
<body>
<span style="font-family: verdana,sans-serif;">
This intention used to specify annotations for parameters and return type of a function.
<p/>
If there is type information collected in run-time, it is used to set the default values of types.
</span>
</body>
</html>
@@ -185,6 +185,11 @@
<category>Python</category>
</intentionAction>
<intentionAction>
<className>com.jetbrains.python.codeInsight.intentions.PyAnnotateTypesIntention</className>
<category>Python</category>
</intentionAction>
<intentionAction>
<className>com.jetbrains.python.codeInsight.intentions.TypeAssertionIntention</className>
<category>Python</category>
@@ -375,6 +380,8 @@
<localInspection language="Python" shortName="PyPep8NamingInspection" suppressId="PyPep8Naming" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
<localInspection language="Python" shortName="PyAssignmentToLoopOrWithParameterInspection" suppressId="PyAssignmentToLoopOrWithParameter" displayName="Assignment to 'for' loop or 'with' statement parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToLoopOrWithParameterInspection"/>
<localInspection language="Python" shortName="PyMissingTypeHintsInspection" suppressId="PyMissingTypeHints" displayName="Type hinting is missing for function definition" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMissingTypeHintsInspection"/>
<defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType$General"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType$Class"/>
@@ -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
@@ -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<PyFunction, Boolean>() {
@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<Pair<Integer, String>> 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<Integer, String> 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");
}
}
@@ -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
@@ -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
*
* <p>
* 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
@@ -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<PyFunction, Boolean>() {
@Override
public Boolean apply(PyFunction input) {
return !isReturnTypeDefined(input);
}
});
}
static boolean resolvesToFunction(@NotNull PsiElement elementAt, Function<PyFunction, Boolean> 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<results.length; i++) {
if (results[i].getElement() instanceof PyFunction) {
final PsiElement result = results[i].getElement();
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;
}
}
return isAvailableForFunction.apply((PyFunction)result);
}
return !isReturnTypeDefined((PyFunction)result);
}
}
return false;
@@ -148,7 +159,7 @@ public abstract class TypeIntention implements IntentionAction {
}
@Nullable
protected static PyCallExpression getCallExpression(PsiElement elementAt) {
static PyCallExpression getCallExpression(PsiElement elementAt) {
final PyExpression problemElement = getProblemElement(elementAt);
if (problemElement != null) {
PsiReference reference = problemElement.getReference();
@@ -172,7 +183,7 @@ public abstract class TypeIntention implements IntentionAction {
}
@Nullable
protected PyCallable getCallable(PsiElement elementAt) {
static PyCallable getCallable(PsiElement elementAt) {
PyCallExpression callExpression = getCallExpression(elementAt);
if (callExpression != null && elementAt != null) {
@@ -182,7 +193,7 @@ public abstract class TypeIntention implements IntentionAction {
return PsiTreeUtil.getParentOfType(elementAt, PyFunction.class);
}
protected PyResolveContext getResolveContext(@NotNull PsiElement origin) {
protected static PyResolveContext getResolveContext(@NotNull PsiElement origin) {
return PyResolveContext.defaultContext().withTypeEvalContext(TypeEvalContext.codeAnalysis(origin.getProject(), origin.getContainingFile()));
}
@@ -16,6 +16,7 @@
package com.jetbrains.python.debugger;
import com.intellij.openapi.util.Ref;
import com.jetbrains.python.psi.PyCallable;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.types.*;
@@ -25,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
* @author traff
*/
public class PyCallSignatureTypeProvider extends PyTypeProviderBase {
@Override
public Ref<PyType> 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<PyType> 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));
}
}
}
}
@@ -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<String> 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 <name>:<type> format. " + parts[i] + " instead.");
throw new IllegalStateException("Should be <name>:<type> format for arg or <type> 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;
@@ -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 = "";
@@ -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);
}
}
}
}
@@ -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;
}
@@ -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));
}
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
+4
View File
@@ -0,0 +1,4 @@
def foo(x):
return x
foo(1)
+11
View File
@@ -0,0 +1,11 @@
def foo(x):
"""
:param x:
:type x: int
:return:
:rtype: int
"""
return x
foo(1)
@@ -0,0 +1,2 @@
def <weak_warning descr="Type hinting is missing for function definition">foo</weak_warning>(x, y):
pass
@@ -0,0 +1,2 @@
def foo(x: int, y: int) -> str:
pass
@@ -0,0 +1,3 @@
def foo(x):
# type: (int) -> int
pass
@@ -0,0 +1,2 @@
def foo(x): # type: (int) -> int
pass
@@ -0,0 +1,2 @@
def fo<caret>o(x, y):
pass
@@ -0,0 +1,2 @@
def foo(x: object, y: object) -> object:
pass
@@ -0,0 +1,3 @@
from foo_decl import foo
fo<caret>o(1, 1)
@@ -0,0 +1,3 @@
from foo_decl import foo
foo(1, 1)
@@ -0,0 +1,5 @@
def foo(x, y):
pass
fo<caret>o(1, 1)
@@ -0,0 +1,5 @@
def foo(x: object, y: object) -> object:
pass
foo(1, 1)
@@ -0,0 +1,2 @@
def foo(x, y):
pass
@@ -0,0 +1,2 @@
def foo(x: object, y: object) -> object:
pass
@@ -0,0 +1,2 @@
def fo<caret>o(x, y):
pass
@@ -0,0 +1,3 @@
def foo(x, y):
# type: (object, object) -> object
pass
@@ -0,0 +1,2 @@
def fo<caret>o(x, y):
pass
@@ -0,0 +1,2 @@
def foo(x, y) -> object:
pass
@@ -0,0 +1,3 @@
from foo_decl import foo
fo<caret>o(1, 1)
@@ -0,0 +1,3 @@
from foo_decl import foo
foo(1, 1)
@@ -0,0 +1,5 @@
def foo(x, y):
pass
fo<caret>o(1, 1)
@@ -0,0 +1,5 @@
def foo(x, y) -> object:
pass
foo(1, 1)
@@ -0,0 +1,3 @@
def foo(var):
print(va<caret>r)
pass
@@ -0,0 +1,3 @@
def foo(var: object):
print(var)
pass
@@ -0,0 +1,2 @@
def foo(x, y):
pass
@@ -0,0 +1,2 @@
def foo(x, y) -> object:
pass
+1 -1
View File
@@ -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) {
+3
View File
@@ -50,6 +50,9 @@ public abstract class PyTestTask {
public void after() throws Exception {
}
public void doFinally() {
}
public void useNormalTimeout() {
}
@@ -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");
});
}
});
}
}
@@ -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) {
@@ -316,6 +316,8 @@ public abstract class PyBaseDebuggerTask extends PyExecutionFixtureTestTask {
throw new RuntimeException(output(), e);
}
finally {
doFinally();
clearAllBreakpoints();
setProcessCanTerminate(true);
@@ -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);
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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);
@@ -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);
}
}
@@ -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);
}