mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
-55
@@ -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);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -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");
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -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
|
||||
|
||||
+76
-29
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-19
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(x):
|
||||
return x
|
||||
|
||||
foo(1)
|
||||
@@ -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)
|
||||
+3
@@ -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
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def fo<caret>o(x, y):
|
||||
pass
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def foo(x, y) -> object:
|
||||
pass
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
from foo_decl import foo
|
||||
|
||||
fo<caret>o(1, 1)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
from foo_decl import foo
|
||||
|
||||
foo(1, 1)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
def foo(x, y):
|
||||
pass
|
||||
|
||||
|
||||
fo<caret>o(1, 1)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
def foo(x, y) -> object:
|
||||
pass
|
||||
|
||||
|
||||
foo(1, 1)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
def foo(var):
|
||||
print(va<caret>r)
|
||||
pass
|
||||
+3
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
+60
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user