Skip to content

Commit 6f59018

Browse files
committed
Forgot to pull before I updated the changelog, still getting used to hg
2 parents e20e24e + 4317962 commit 6f59018

File tree

4 files changed

+244
-93
lines changed

4 files changed

+244
-93
lines changed

AUTHORS

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
bpython is written and maintained by Bob Farrell
2+
<robertanthonyfarrell at gmail dot com>.
3+
4+
5+
Other contributors are (in alphabetical order):
6+
7+
* Pavel Panchekha <pavpanchekha at gmail dot com>
8+
* Andreas Stuehrk <andy-python at hammerhartes dot de>
9+
10+
11+
Many thanks for all contributions!

bpython/cli.py

Lines changed: 83 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@
4949
from cStringIO import StringIO
5050
from urlparse import urljoin
5151
from xmlrpclib import ServerProxy, Error as XMLRPCError
52+
from ConfigParser import ConfigParser, NoSectionError, NoOptionError
5253

5354
# These are used for syntax hilighting.
54-
from pygments import highlight
55+
from pygments import format
5556
from pygments.lexers import PythonLexer
57+
from pygments.token import Token
5658
from bpython.formatter import BPythonFormatter
59+
from itertools import chain
60+
61+
# This for import completion
62+
from bpython import importcompletion
5763

5864
# And these are used for argspec.
5965
from pyparsing import Forward, Suppress, QuotedString, dblQuotedString, \
@@ -109,24 +115,12 @@ def readlines(self, x):
109115
OPTS = Struct()
110116
DO_RESIZE = False
111117

112-
113-
# Set default values. (Overridden by loadrc())
114-
OPTS.tab_length = 4
115-
OPTS.auto_display_list = True
116-
OPTS.syntax = True
117-
OPTS.arg_spec = True
118-
OPTS.hist_file = '~/.pythonhist'
119-
OPTS.hist_length = 100
120-
OPTS.flush_output = True
121-
122118
# TODO:
123119
#
124120
# C-l doesn't repaint the screen yet.
125121
#
126122
# Tab completion does not work if not at the end of the line.
127123
#
128-
# Triple-quoted strings over multiple lines are not colourised correctly.
129-
#
130124
# Numerous optimisations can be made but it seems to do all the lookup stuff
131125
# fast enough on even my crappy server so I'm not too bothered about that
132126
# at the moment.
@@ -168,6 +162,18 @@ def make_colours():
168162
return c
169163

170164

165+
def next_token_inside_string(s, inside_string):
166+
"""Given a code string s and an initial state inside_string, return
167+
whether the next token will be inside a string or not."""
168+
for token, value in PythonLexer().get_tokens(s):
169+
if token is Token.String and value in ['"""', "'''", '"', "'"]:
170+
if not inside_string:
171+
inside_string = value
172+
elif value == inside_string:
173+
inside_string = False
174+
return inside_string
175+
176+
171177
class Interpreter(code.InteractiveInterpreter):
172178

173179
def __init__(self):
@@ -194,6 +200,7 @@ def showsyntaxerror(self, filename=None):
194200
sys.last_type = type
195201
sys.last_value = value
196202
if filename and type is SyntaxError:
203+
self.inside_string = False
197204
# Work hard to stuff the correct filename in the exception
198205
try:
199206
msg, (dummy_filename, lineno, offset, line) = value
@@ -302,6 +309,7 @@ def __init__(self, scr, interp, statusbar=None, idle=None):
302309
self.matches = []
303310
self.argspec = None
304311
self.s = ''
312+
self.inside_string = False
305313
self.list_win_visible = False
306314
self._C = {}
307315
sys.stdin = FakeStdin(self)
@@ -535,26 +543,31 @@ def _complete(self, unused_tab=False):
535543
if not cw:
536544
self.matches = []
537545

538-
try:
539-
self.completer.complete(cw, 0)
540-
except Exception:
546+
# Check for import completion
547+
e = False
548+
matches = importcompletion.complete(self.s, cw)
549+
if not matches:
550+
# Nope, no import, continue with normal completion
551+
try:
552+
self.completer.complete(cw, 0)
553+
except Exception:
541554
# This sucks, but it's either that or list all the exceptions that could
542555
# possibly be raised here, so if anyone wants to do that, feel free to send me
543556
# a patch. XXX: Make sure you raise here if you're debugging the completion
544557
# stuff !
545-
e = True
546-
else:
547-
e = False
558+
e = True
559+
else:
560+
matches = self.completer.matches
548561

549-
if e or not self.completer.matches:
562+
if e or not matches:
550563
self.matches = []
551564
if not self.argspec:
552565
self.scr.redrawwin()
553566
return False
554567

555-
if not e and self.completer.matches:
568+
if not e and matches:
556569
# remove duplicates and restore order
557-
self.matches = sorted(set(self.completer.matches))
570+
self.matches = sorted(set(matches))
558571

559572
if len(self.matches) == 1 and not OPTS.auto_display_list:
560573
self.list_win_visible = True
@@ -744,13 +757,15 @@ def mkargspec(self, topline, down):
744757
self.list_win.addstr(', ', curses.color_pair(self._C["g"]+1))
745758

746759
if _args:
747-
self.list_win.addstr(', ',
748-
curses.color_pair(self._C["g"]+1))
760+
if args:
761+
self.list_win.addstr(', ',
762+
curses.color_pair(self._C["g"]+1))
749763
self.list_win.addstr('*%s' % (_args, ),
750764
curses.color_pair(self._C["m"]+1))
751765
if _kwargs:
752-
self.list_win.addstr(', ',
753-
curses.color_pair(self._C["g"]+1))
766+
if args or _args:
767+
self.list_win.addstr(', ',
768+
curses.color_pair(self._C["g"]+1))
754769
self.list_win.addstr('**%s' % (_kwargs, ),
755770
curses.color_pair(self._C["m"]+1))
756771
self.list_win.addstr(')', curses.color_pair(self._C["y"]+1))
@@ -1376,6 +1391,9 @@ def lf(self):
13761391
for _ in range(self.cpos):
13771392
self.mvc(-1)
13781393

1394+
self.inside_string = next_token_inside_string(self.s,
1395+
self.inside_string)
1396+
13791397
self.echo("\n")
13801398

13811399
def addstr(self, s):
@@ -1397,7 +1415,16 @@ def print_line(self, s, clr=False):
13971415
clr = True
13981416

13991417
if OPTS.syntax:
1400-
o = highlight(s, PythonLexer(), BPythonFormatter())
1418+
if self.inside_string:
1419+
# A string started in another line is continued in this
1420+
# line
1421+
tokens = PythonLexer().get_tokens(self.inside_string + s)
1422+
token, value = tokens.next()
1423+
if token is Token.String.Doc:
1424+
tokens = chain([(Token.String, value[3:])], tokens)
1425+
else:
1426+
tokens = PythonLexer().get_tokens(s)
1427+
o = format(tokens, BPythonFormatter())
14011428
else:
14021429
o = s
14031430

@@ -1683,6 +1710,7 @@ def idle(caller):
16831710

16841711
global stdscr
16851712

1713+
importcompletion.find_coroutine()
16861714
caller.statusbar.check()
16871715

16881716
if DO_RESIZE:
@@ -1707,59 +1735,38 @@ def do_resize(caller):
17071735
caller.resize()
17081736
# The list win resizes itself every time it appears so no need to do it here.
17091737

1710-
1711-
def loadrc():
1712-
"""Use the shlex module to make a simple lexer for the settings,
1713-
it also attempts to convert any integers to Python ints, otherwise
1714-
leaves them as strings and handles hopefully all the sane ways of
1715-
representing a boolean."""
1716-
1738+
def loadini():
1739+
"""Loads .ini configuration file and stores its values in OPTS"""
1740+
class CP(ConfigParser):
1741+
def safeget(self, section, option, default):
1742+
"""safet get method using default values"""
1743+
try:
1744+
v = self.get(section, option)
1745+
except NoSectionError, NoOptionError:
1746+
v = default
1747+
if isinstance(v, bool):
1748+
return v
1749+
try:
1750+
return int(v)
1751+
except ValueError:
1752+
return v
1753+
17171754
if len(sys.argv) > 2:
17181755
path = sys.argv[2]
17191756
else:
1720-
path = os.path.expanduser('~/.bpythonrc')
1721-
1722-
if not os.path.isfile(path):
1723-
return
1724-
1725-
f = open(path)
1726-
parser = shlex.shlex(f)
1727-
1728-
bools = {
1729-
'true': True,
1730-
'yes': True,
1731-
'on': True,
1732-
'false': False,
1733-
'no': False,
1734-
'off': False
1735-
}
1736-
1737-
config = {}
1738-
while True:
1739-
k = parser.get_token()
1740-
v = None
1741-
1742-
if not k:
1743-
break
1744-
1745-
k = k.lower()
1746-
1747-
if parser.get_token() == '=':
1748-
v = parser.get_token() or None
1749-
1750-
if v is not None:
1751-
try:
1752-
v = int(v)
1753-
except ValueError:
1754-
if v.lower() in bools:
1755-
v = bools[v.lower()]
1757+
configfile = os.path.expanduser('~/.bpython.ini')
1758+
1759+
config = CP()
1760+
config.read(configfile)
17561761

1757-
config[k] = v
1758-
f.close()
1762+
OPTS.tab_length = config.safeget('general', 'tab_length', 4)
1763+
OPTS.auto_display_list = config.safeget('general', 'auto_display_list', True)
1764+
OPTS.syntax = config.safeget('general', 'syntax', True)
1765+
OPTS.arg_spec = config.safeget('general', 'arg_spec', True)
1766+
OPTS.hist_file = config.safeget('general', 'hist_file', '~/.pythonhist')
1767+
OPTS.hist_length = config.safeget('general', 'hist_length', 100)
1768+
OPTS.flush_output = config.safeget('general', 'flush_output', True)
17591769

1760-
for k, v in config.iteritems():
1761-
if hasattr(OPTS, k):
1762-
setattr(OPTS, k, v)
17631770

17641771
class FakeDict(object):
17651772
"""Very simple dict-alike that returns a constant value for any key -
@@ -1784,7 +1791,7 @@ def main_curses(scr):
17841791
global DO_RESIZE
17851792
DO_RESIZE = False
17861793
signal.signal(signal.SIGWINCH, lambda *_: sigwinch(scr))
1787-
loadrc()
1794+
loadini()
17881795
stdscr = scr
17891796
try:
17901797
curses.start_color()

bpython/formatter.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,40 @@
5353
"""
5454

5555
f_strings = {
56-
Keyword: "\x01y\x03%s\x04",
57-
Name: "\x01w\x02\x03%s\x04",
58-
Comment: "\x01b\x03%s\x04",
59-
String: "\x01m\x03%s\x04",
60-
Error: "\x01r\x03%s\x04",
61-
Literal: "\x01r\x03%s\x04",
62-
Literal.String: "\x01m\x03%s\x04",
63-
Token.Literal.Number.Float: "\x01g\x02\x03%s\x04",
64-
Number: "\x01g\x03%s\x04",
65-
Operator: "\x01c\x02\x03%s\x04",
66-
Operator.Word: "\x01c\x02\x03%s\x04",
67-
Punctuation: "\x01c\x02\x03%s\x04",
68-
Generic: "\x01d\x03%s\x04",
69-
Token: "\x01g\x03%s\x04",
70-
Whitespace: "\x02d\x03%s\x04",
56+
Keyword: "\x01y",
57+
Name: "\x01w\x02",
58+
Comment: "\x01b",
59+
String: "\x01m",
60+
Error: "\x01r",
61+
Literal: "\x01r",
62+
Literal.String: "\x01m",
63+
Token.Literal.Number.Float: "\x01g\x02",
64+
Number: "\x01g",
65+
Operator: "\x01c\x02",
66+
Operator.Word: "\x01c\x02",
67+
Punctuation: "\x01c\x02",
68+
Generic: "\x01d",
69+
Token: "\x01g",
70+
Whitespace: "\x02d",
7171
}
7272

73+
f_strings_light = {
74+
Keyword: "\x01b",
75+
Name: "\x01k\x02",
76+
Comment: "\x01b",
77+
String: "\x01g",
78+
Error: "\x01r",
79+
Literal: "\x01r",
80+
Literal.String: "\x01g",
81+
Token.Literal.Number.Float: "\x01g\x02",
82+
Number: "\x01g",
83+
Operator: "\x01b\x02",
84+
Operator.Word: "\x01k\x02",
85+
Punctuation: "\x01b\x02",
86+
Generic: "\x01d",
87+
Token: "\x01b",
88+
Whitespace: "\x02d",
89+
}
7390

7491
class BPythonFormatter(Formatter):
7592
"""This is the custom formatter for bpython.
@@ -93,9 +110,9 @@ def format(self, tokensource, outfile):
93110
continue
94111

95112
if token in f_strings:
96-
o += f_strings[token] % (text, )
113+
o += "%s\x03%s\x04" % (f_strings[token], text )
97114
else:
98-
o += f_strings[Token] % (text, )
115+
o += "%s\x03%s\x04" % (f_strings[Token], text )
99116
outfile.write(o.rstrip())
100117

101118
# vim: sw=4 ts=4 sts=4 ai et

0 commit comments

Comments
 (0)