forked from prompt-toolkit/ptpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutils.py
More file actions
162 lines (127 loc) · 4.68 KB
/
utils.py
File metadata and controls
162 lines (127 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
For internal use only.
"""
import re
from typing import Callable, Type, TypeVar, cast
from prompt_toolkit.formatted_text import to_formatted_text
from prompt_toolkit.formatted_text.utils import fragment_list_to_text
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
__all__ = [
"has_unclosed_brackets",
"get_jedi_script_from_document",
"document_is_multiline_python",
]
def has_unclosed_brackets(text: str) -> bool:
"""
Starting at the end of the string. If we find an opening bracket
for which we didn't had a closing one yet, return True.
"""
stack = []
# Ignore braces inside strings
text = re.sub(r"""('[^']*'|"[^"]*")""", "", text) # XXX: handle escaped quotes.!
for c in reversed(text):
if c in "])}":
stack.append(c)
elif c in "[({":
if stack:
if (
(c == "[" and stack[-1] == "]")
or (c == "{" and stack[-1] == "}")
or (c == "(" and stack[-1] == ")")
):
stack.pop()
else:
# Opening bracket for which we didn't had a closing one.
return True
return False
def get_jedi_script_from_document(document, locals, globals):
import jedi # We keep this import in-line, to improve start-up time.
# Importing Jedi is 'slow'.
try:
return jedi.Interpreter(
document.text,
column=document.cursor_position_col,
line=document.cursor_position_row + 1,
path="input-text",
namespaces=[locals, globals],
)
except ValueError:
# Invalid cursor position.
# ValueError('`column` parameter is not in a valid range.')
return None
except AttributeError:
# Workaround for #65: https://github.com/jonathanslenders/python-prompt-toolkit/issues/65
# See also: https://github.com/davidhalter/jedi/issues/508
return None
except IndexError:
# Workaround Jedi issue #514: for https://github.com/davidhalter/jedi/issues/514
return None
except KeyError:
# Workaroud for a crash when the input is "u'", the start of a unicode string.
return None
except Exception:
# Workaround for: https://github.com/jonathanslenders/ptpython/issues/91
return None
_multiline_string_delims = re.compile("""[']{3}|["]{3}""")
def document_is_multiline_python(document):
"""
Determine whether this is a multiline Python document.
"""
def ends_in_multiline_string() -> bool:
"""
``True`` if we're inside a multiline string at the end of the text.
"""
delims = _multiline_string_delims.findall(document.text)
opening = None
for delim in delims:
if opening is None:
opening = delim
elif delim == opening:
opening = None
return bool(opening)
if "\n" in document.text or ends_in_multiline_string():
return True
def line_ends_with_colon() -> bool:
return document.current_line.rstrip()[-1:] == ":"
# If we just typed a colon, or still have open brackets, always insert a real newline.
if (
line_ends_with_colon()
or (
document.is_cursor_at_the_end
and has_unclosed_brackets(document.text_before_cursor)
)
or document.text.startswith("@")
):
return True
# If the character before the cursor is a backslash (line continuation
# char), insert a new line.
elif document.text_before_cursor[-1:] == "\\":
return True
return False
_T = TypeVar("_T", bound=Callable[[MouseEvent], None])
def if_mousedown(handler: _T) -> _T:
"""
Decorator for mouse handlers.
Only handle event when the user pressed mouse down.
(When applied to a token list. Scroll events will bubble up and are handled
by the Window.)
"""
def handle_if_mouse_down(mouse_event: MouseEvent):
if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
return handler(mouse_event)
else:
return NotImplemented
return cast(_T, handle_if_mouse_down)
_T_type = TypeVar("_T_type", bound=Type)
def ptrepr_to_repr(cls: _T_type) -> _T_type:
"""
Generate a normal `__repr__` method for classes that have a `__pt_repr__`.
"""
if not hasattr(cls, "__pt_repr__"):
raise TypeError(
"@ptrepr_to_repr can only be applied to classes that have a `__pt_repr__` method."
)
def __repr__(self) -> str:
return fragment_list_to_text(to_formatted_text(cls.__pt_repr__(self)))
cls.__repr__ = __repr__ # type:ignore
return cls