mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 16:34:47 +01:00
Merge pull request #696 from scopatz/rprompt
right prompt implementation
This commit is contained in:
commit
76fcd989ee
11 changed files with 603 additions and 14 deletions
|
@ -23,6 +23,8 @@ Current Developments
|
||||||
* ``?`` and ``??`` operator output now has colored titles, like in IPython.
|
* ``?`` and ``??`` operator output now has colored titles, like in IPython.
|
||||||
* ``??`` will syntax highlight source code if pygments is available.
|
* ``??`` will syntax highlight source code if pygments is available.
|
||||||
* Python mode output is now syntax highlighted if pygments is available.
|
* Python mode output is now syntax highlighted if pygments is available.
|
||||||
|
* New ``$RIGHT_PROMPT`` environment variable for displaying right-aligned
|
||||||
|
text in prompt-toolkit shell.
|
||||||
|
|
||||||
**Changed:**
|
**Changed:**
|
||||||
|
|
||||||
|
|
|
@ -875,6 +875,7 @@ keyword arguments, which will be replaced automatically:
|
||||||
snail@home:~ >> # so does that!
|
snail@home:~ >> # so does that!
|
||||||
|
|
||||||
By default, the following variables are available for use:
|
By default, the following variables are available for use:
|
||||||
|
|
||||||
* ``user``: The username of the current user
|
* ``user``: The username of the current user
|
||||||
* ``hostname``: The name of the host computer
|
* ``hostname``: The name of the host computer
|
||||||
* ``cwd``: The current working directory
|
* ``cwd``: The current working directory
|
||||||
|
@ -895,13 +896,35 @@ By default, the following variables are available for use:
|
||||||
You can also color your prompt easily by inserting keywords such as ``{GREEN}``
|
You can also color your prompt easily by inserting keywords such as ``{GREEN}``
|
||||||
or ``{BOLD_BLUE}``. Colors have the form shown below:
|
or ``{BOLD_BLUE}``. Colors have the form shown below:
|
||||||
|
|
||||||
* ``(QUALIFIER\_)COLORNAME``: Inserts an ANSI color code
|
* ``NO_COLOR``: Resets any previously used color codes
|
||||||
* ``COLORNAME`` can be any of: ``BLACK``, ``RED``, ``GREEN``, ``YELLOW``,
|
* ``COLORNAME``: Inserts a color code for the following basic colors,
|
||||||
``BLUE``, ``PURPLE``, ``CYAN``, or ``WHITE``
|
which come in regular (dark) and intense (light) forms:
|
||||||
* ``QUALIFIER`` is optional and can be any of: ``BOLD``, ``UNDERLINE``,
|
|
||||||
``BACKGROUND``, ``INTENSE``, ``BOLD_INTENSE``, or
|
- ``BLACK`` or ``INTENSE_BLACK``
|
||||||
``BACKGROUND_INTENSE``
|
- ``RED`` or ``INTENSE_RED``
|
||||||
* ``NO_COLOR``: Resets any previously used color codes
|
- ``GREEN`` or ``INTENSE_GREEN``
|
||||||
|
- ``YELLOW`` or ``INTENSE_YELLOW``
|
||||||
|
- ``BLUE`` or ``INTENSE_BLUE``
|
||||||
|
- ``PURPLE`` or ``INTENSE_PURPLE``
|
||||||
|
- ``CYAN`` or ``INTENSE_CYAN``
|
||||||
|
- ``WHITE`` or ``INTENSE_WHITE``
|
||||||
|
|
||||||
|
* ``#HEX``: A ``#`` before a len-3 or len-6 hex code will use that
|
||||||
|
hex color, or the nearest approximation that that is supported by
|
||||||
|
the shell and terminal. For example, ``#fff`` and ``#fafad2`` are
|
||||||
|
both valid.
|
||||||
|
* ``BACKGROUND_`` may be added to the begining of a color name or hex
|
||||||
|
color to set a background color. For example, ``BACKGROUND_INTENSE_RED``
|
||||||
|
and ``BACKGROUND_#123456`` can both be used.
|
||||||
|
* ``bg#HEX`` or ``BG#HEX`` are shortcuts for setting a background hex color.
|
||||||
|
Thus you can set ``bg#0012ab`` or the uppercase version.
|
||||||
|
* ``BOLD_`` is a prefix qualifier that may be used with any foreground color.
|
||||||
|
For example, ``BOLD_RED`` and ``BOLD_#112233`` are OK!
|
||||||
|
* ``UNDERLINE_`` is a prefix qualifier that also may be used with any
|
||||||
|
foreground color. For example, ``UNDERLINE_GREEN``.
|
||||||
|
* Or any other combination of qualifiers, such as
|
||||||
|
``BOLD_UNDERLINE_INTENSE_BLACK``, which is the most metal color you
|
||||||
|
can use!
|
||||||
|
|
||||||
You can make use of additional variables beyond these by adding them to the
|
You can make use of additional variables beyond these by adding them to the
|
||||||
``FORMATTER_DICT`` environment variable. The values in this dictionary
|
``FORMATTER_DICT`` environment variable. The values in this dictionary
|
||||||
|
|
|
@ -6,6 +6,7 @@ import glob
|
||||||
import builtins
|
import builtins
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from nose.plugins.skip import SkipTest
|
from nose.plugins.skip import SkipTest
|
||||||
|
@ -23,10 +24,13 @@ ON_MAC = (platform.system() == 'Darwin')
|
||||||
def sp(cmd):
|
def sp(cmd):
|
||||||
return subprocess.check_output(cmd, universal_newlines=True)
|
return subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
|
class DummyStyler():
|
||||||
|
styles = defaultdict(None.__class__)
|
||||||
|
|
||||||
class DummyBaseShell(BaseShell):
|
class DummyBaseShell(BaseShell):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
self.styler = DummyStyler()
|
||||||
|
|
||||||
|
|
||||||
class DummyShell:
|
class DummyShell:
|
||||||
|
|
|
@ -75,7 +75,7 @@ def source_foreign(args, stdin=None):
|
||||||
"""Sources a file written in a foreign shell language."""
|
"""Sources a file written in a foreign shell language."""
|
||||||
parser = _ensure_source_foreign_parser()
|
parser = _ensure_source_foreign_parser()
|
||||||
ns = parser.parse_args(args)
|
ns = parser.parse_args(args)
|
||||||
if ns.prevcmd is not None:
|
if ns.prevcmd is not None:
|
||||||
pass # don't change prevcmd if given explicitly
|
pass # don't change prevcmd if given explicitly
|
||||||
elif os.path.isfile(ns.files_or_code[0]):
|
elif os.path.isfile(ns.files_or_code[0]):
|
||||||
# we have filename to source
|
# we have filename to source
|
||||||
|
@ -191,6 +191,11 @@ def vox(args, stdin=None):
|
||||||
vox = Vox()
|
vox = Vox()
|
||||||
return vox(args, stdin=stdin)
|
return vox(args, stdin=stdin)
|
||||||
|
|
||||||
|
def mpl(args, stdin=None):
|
||||||
|
"""Hooks to matplotlib"""
|
||||||
|
from xonsh.mplhooks import show
|
||||||
|
show()
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_ALIASES = {
|
DEFAULT_ALIASES = {
|
||||||
'cd': cd,
|
'cd': cd,
|
||||||
|
@ -212,6 +217,7 @@ DEFAULT_ALIASES = {
|
||||||
'replay': replay_main,
|
'replay': replay_main,
|
||||||
'!!': bang_bang,
|
'!!': bang_bang,
|
||||||
'!n': bang_n,
|
'!n': bang_n,
|
||||||
|
'mpl': mpl,
|
||||||
'trace': trace,
|
'trace': trace,
|
||||||
'timeit': timeit_alias,
|
'timeit': timeit_alias,
|
||||||
'xonfig': xonfig,
|
'xonfig': xonfig,
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
"""Tools for helping with ANSI color codes."""
|
"""Tools for helping with ANSI color codes."""
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
|
RE_BACKGROUND = re.compile('(bg|bg#|bghex|background)')
|
||||||
|
|
||||||
def partial_color_format(template, style='default', cmap=None, hide=False):
|
def partial_color_format(template, style='default', cmap=None, hide=False):
|
||||||
"""Formats a template string but only with respect to the colors.
|
"""Formats a template string but only with respect to the colors.
|
||||||
Another template string is returned, with the color values filled in.
|
Another template string is returned, with the color values filled in.
|
||||||
|
@ -42,8 +45,23 @@ def partial_color_format(template, style='default', cmap=None, hide=False):
|
||||||
toks = []
|
toks = []
|
||||||
for literal, field, spec, conv in formatter.parse(template):
|
for literal, field, spec, conv in formatter.parse(template):
|
||||||
toks.append(literal)
|
toks.append(literal)
|
||||||
if field in cmap:
|
if field is None:
|
||||||
|
pass
|
||||||
|
elif field in cmap:
|
||||||
toks.extend([esc, cmap[field], m])
|
toks.extend([esc, cmap[field], m])
|
||||||
|
elif '#' in field:
|
||||||
|
field = field.lower()
|
||||||
|
pre, _, post = field.partition('#')
|
||||||
|
f_or_b = '38' if RE_BACKGROUND.search(pre) is None else '48'
|
||||||
|
rgb, _, post = post.partition('_')
|
||||||
|
c256, _ = rgb_to_256(rgb)
|
||||||
|
color = f_or_b + ';5;' + c256
|
||||||
|
mods = pre + '_' + post
|
||||||
|
if 'underline' in mods:
|
||||||
|
color = '4;' + color
|
||||||
|
if 'bold' in mods:
|
||||||
|
color = '1;' + color
|
||||||
|
toks.extend([esc, color, m])
|
||||||
elif field is not None:
|
elif field is not None:
|
||||||
toks.append(bopen)
|
toks.append(bopen)
|
||||||
toks.append(field)
|
toks.append(field)
|
||||||
|
@ -56,6 +74,294 @@ def partial_color_format(template, style='default', cmap=None, hide=False):
|
||||||
toks.append(bclose)
|
toks.append(bclose)
|
||||||
return ''.join(toks)
|
return ''.join(toks)
|
||||||
|
|
||||||
|
RGB_256 = {
|
||||||
|
'000000': '16',
|
||||||
|
'00005f': '17',
|
||||||
|
'000080': '04',
|
||||||
|
'000087': '18',
|
||||||
|
'0000af': '19',
|
||||||
|
'0000d7': '20',
|
||||||
|
'0000ff': '21',
|
||||||
|
'005f00': '22',
|
||||||
|
'005f5f': '23',
|
||||||
|
'005f87': '24',
|
||||||
|
'005faf': '25',
|
||||||
|
'005fd7': '26',
|
||||||
|
'005fff': '27',
|
||||||
|
'008000': '02',
|
||||||
|
'008080': '06',
|
||||||
|
'008700': '28',
|
||||||
|
'00875f': '29',
|
||||||
|
'008787': '30',
|
||||||
|
'0087af': '31',
|
||||||
|
'0087d7': '32',
|
||||||
|
'0087ff': '33',
|
||||||
|
'00af00': '34',
|
||||||
|
'00af5f': '35',
|
||||||
|
'00af87': '36',
|
||||||
|
'00afaf': '37',
|
||||||
|
'00afd7': '38',
|
||||||
|
'00afff': '39',
|
||||||
|
'00d700': '40',
|
||||||
|
'00d75f': '41',
|
||||||
|
'00d787': '42',
|
||||||
|
'00d7af': '43',
|
||||||
|
'00d7d7': '44',
|
||||||
|
'00d7ff': '45',
|
||||||
|
'00ff00': '46',
|
||||||
|
'00ff5f': '47',
|
||||||
|
'00ff87': '48',
|
||||||
|
'00ffaf': '49',
|
||||||
|
'00ffd7': '50',
|
||||||
|
'00ffff': '51',
|
||||||
|
'080808': '232',
|
||||||
|
'121212': '233',
|
||||||
|
'1c1c1c': '234',
|
||||||
|
'262626': '235',
|
||||||
|
'303030': '236',
|
||||||
|
'3a3a3a': '237',
|
||||||
|
'444444': '238',
|
||||||
|
'4e4e4e': '239',
|
||||||
|
'585858': '240',
|
||||||
|
'5f0000': '52',
|
||||||
|
'5f005f': '53',
|
||||||
|
'5f0087': '54',
|
||||||
|
'5f00af': '55',
|
||||||
|
'5f00d7': '56',
|
||||||
|
'5f00ff': '57',
|
||||||
|
'5f5f00': '58',
|
||||||
|
'5f5f5f': '59',
|
||||||
|
'5f5f87': '60',
|
||||||
|
'5f5faf': '61',
|
||||||
|
'5f5fd7': '62',
|
||||||
|
'5f5fff': '63',
|
||||||
|
'5f8700': '64',
|
||||||
|
'5f875f': '65',
|
||||||
|
'5f8787': '66',
|
||||||
|
'5f87af': '67',
|
||||||
|
'5f87d7': '68',
|
||||||
|
'5f87ff': '69',
|
||||||
|
'5faf00': '70',
|
||||||
|
'5faf5f': '71',
|
||||||
|
'5faf87': '72',
|
||||||
|
'5fafaf': '73',
|
||||||
|
'5fafd7': '74',
|
||||||
|
'5fafff': '75',
|
||||||
|
'5fd700': '76',
|
||||||
|
'5fd75f': '77',
|
||||||
|
'5fd787': '78',
|
||||||
|
'5fd7af': '79',
|
||||||
|
'5fd7d7': '80',
|
||||||
|
'5fd7ff': '81',
|
||||||
|
'5fff00': '82',
|
||||||
|
'5fff5f': '83',
|
||||||
|
'5fff87': '84',
|
||||||
|
'5fffaf': '85',
|
||||||
|
'5fffd7': '86',
|
||||||
|
'5fffff': '87',
|
||||||
|
'626262': '241',
|
||||||
|
'6c6c6c': '242',
|
||||||
|
'767676': '243',
|
||||||
|
'800000': '01',
|
||||||
|
'800080': '05',
|
||||||
|
'808000': '03',
|
||||||
|
'808080': '244',
|
||||||
|
'870000': '88',
|
||||||
|
'87005f': '89',
|
||||||
|
'870087': '90',
|
||||||
|
'8700af': '91',
|
||||||
|
'8700d7': '92',
|
||||||
|
'8700ff': '93',
|
||||||
|
'875f00': '94',
|
||||||
|
'875f5f': '95',
|
||||||
|
'875f87': '96',
|
||||||
|
'875faf': '97',
|
||||||
|
'875fd7': '98',
|
||||||
|
'875fff': '99',
|
||||||
|
'878700': '100',
|
||||||
|
'87875f': '101',
|
||||||
|
'878787': '102',
|
||||||
|
'8787af': '103',
|
||||||
|
'8787d7': '104',
|
||||||
|
'8787ff': '105',
|
||||||
|
'87af00': '106',
|
||||||
|
'87af5f': '107',
|
||||||
|
'87af87': '108',
|
||||||
|
'87afaf': '109',
|
||||||
|
'87afd7': '110',
|
||||||
|
'87afff': '111',
|
||||||
|
'87d700': '112',
|
||||||
|
'87d75f': '113',
|
||||||
|
'87d787': '114',
|
||||||
|
'87d7af': '115',
|
||||||
|
'87d7d7': '116',
|
||||||
|
'87d7ff': '117',
|
||||||
|
'87ff00': '118',
|
||||||
|
'87ff5f': '119',
|
||||||
|
'87ff87': '120',
|
||||||
|
'87ffaf': '121',
|
||||||
|
'87ffd7': '122',
|
||||||
|
'87ffff': '123',
|
||||||
|
'8a8a8a': '245',
|
||||||
|
'949494': '246',
|
||||||
|
'9e9e9e': '247',
|
||||||
|
'a8a8a8': '248',
|
||||||
|
'af0000': '124',
|
||||||
|
'af005f': '125',
|
||||||
|
'af0087': '126',
|
||||||
|
'af00af': '127',
|
||||||
|
'af00d7': '128',
|
||||||
|
'af00ff': '129',
|
||||||
|
'af5f00': '130',
|
||||||
|
'af5f5f': '131',
|
||||||
|
'af5f87': '132',
|
||||||
|
'af5faf': '133',
|
||||||
|
'af5fd7': '134',
|
||||||
|
'af5fff': '135',
|
||||||
|
'af8700': '136',
|
||||||
|
'af875f': '137',
|
||||||
|
'af8787': '138',
|
||||||
|
'af87af': '139',
|
||||||
|
'af87d7': '140',
|
||||||
|
'af87ff': '141',
|
||||||
|
'afaf00': '142',
|
||||||
|
'afaf5f': '143',
|
||||||
|
'afaf87': '144',
|
||||||
|
'afafaf': '145',
|
||||||
|
'afafd7': '146',
|
||||||
|
'afafff': '147',
|
||||||
|
'afd700': '148',
|
||||||
|
'afd75f': '149',
|
||||||
|
'afd787': '150',
|
||||||
|
'afd7af': '151',
|
||||||
|
'afd7d7': '152',
|
||||||
|
'afd7ff': '153',
|
||||||
|
'afff00': '154',
|
||||||
|
'afff5f': '155',
|
||||||
|
'afff87': '156',
|
||||||
|
'afffaf': '157',
|
||||||
|
'afffd7': '158',
|
||||||
|
'afffff': '159',
|
||||||
|
'b2b2b2': '249',
|
||||||
|
'bcbcbc': '250',
|
||||||
|
'c0c0c0': '07',
|
||||||
|
'c6c6c6': '251',
|
||||||
|
'd0d0d0': '252',
|
||||||
|
'd70000': '160',
|
||||||
|
'd7005f': '161',
|
||||||
|
'd70087': '162',
|
||||||
|
'd700af': '163',
|
||||||
|
'd700d7': '164',
|
||||||
|
'd700ff': '165',
|
||||||
|
'd75f00': '166',
|
||||||
|
'd75f5f': '167',
|
||||||
|
'd75f87': '168',
|
||||||
|
'd75faf': '169',
|
||||||
|
'd75fd7': '170',
|
||||||
|
'd75fff': '171',
|
||||||
|
'd78700': '172',
|
||||||
|
'd7875f': '173',
|
||||||
|
'd78787': '174',
|
||||||
|
'd787af': '175',
|
||||||
|
'd787d7': '176',
|
||||||
|
'd787ff': '177',
|
||||||
|
'd7af00': '178',
|
||||||
|
'd7af5f': '179',
|
||||||
|
'd7af87': '180',
|
||||||
|
'd7afaf': '181',
|
||||||
|
'd7afd7': '182',
|
||||||
|
'd7afff': '183',
|
||||||
|
'd7d700': '184',
|
||||||
|
'd7d75f': '185',
|
||||||
|
'd7d787': '186',
|
||||||
|
'd7d7af': '187',
|
||||||
|
'd7d7d7': '188',
|
||||||
|
'd7d7ff': '189',
|
||||||
|
'd7ff00': '190',
|
||||||
|
'd7ff5f': '191',
|
||||||
|
'd7ff87': '192',
|
||||||
|
'd7ffaf': '193',
|
||||||
|
'd7ffd7': '194',
|
||||||
|
'd7ffff': '195',
|
||||||
|
'dadada': '253',
|
||||||
|
'e4e4e4': '254',
|
||||||
|
'eeeeee': '255',
|
||||||
|
'ff0000': '196',
|
||||||
|
'ff005f': '197',
|
||||||
|
'ff0087': '198',
|
||||||
|
'ff00af': '199',
|
||||||
|
'ff00d7': '200',
|
||||||
|
'ff00ff': '201',
|
||||||
|
'ff5f00': '202',
|
||||||
|
'ff5f5f': '203',
|
||||||
|
'ff5f87': '204',
|
||||||
|
'ff5faf': '205',
|
||||||
|
'ff5fd7': '206',
|
||||||
|
'ff5fff': '207',
|
||||||
|
'ff8700': '208',
|
||||||
|
'ff875f': '209',
|
||||||
|
'ff8787': '210',
|
||||||
|
'ff87af': '211',
|
||||||
|
'ff87d7': '212',
|
||||||
|
'ff87ff': '213',
|
||||||
|
'ffaf00': '214',
|
||||||
|
'ffaf5f': '215',
|
||||||
|
'ffaf87': '216',
|
||||||
|
'ffafaf': '217',
|
||||||
|
'ffafd7': '218',
|
||||||
|
'ffafff': '219',
|
||||||
|
'ffd700': '220',
|
||||||
|
'ffd75f': '221',
|
||||||
|
'ffd787': '222',
|
||||||
|
'ffd7af': '223',
|
||||||
|
'ffd7d7': '224',
|
||||||
|
'ffd7ff': '225',
|
||||||
|
'ffff00': '226',
|
||||||
|
'ffff5f': '227',
|
||||||
|
'ffff87': '228',
|
||||||
|
'ffffaf': '229',
|
||||||
|
'ffffd7': '230',
|
||||||
|
'ffffff': '231',
|
||||||
|
}
|
||||||
|
|
||||||
|
RE_RGB3 = re.compile(r'(.)(.)(.)')
|
||||||
|
RE_RGB6 = re.compile(r'(..)(..)(..)')
|
||||||
|
|
||||||
|
def rgb_to_ints(rgb):
|
||||||
|
"""Converts an RGB string into a tuple of ints."""
|
||||||
|
if len(rgb) == 6:
|
||||||
|
return tuple([int(h, 16) for h in RE_RGB6.split(rgb)[1:4]])
|
||||||
|
else:
|
||||||
|
return tuple([int(h*2, 16) for h in RE_RGB3.split(rgb)[1:4]])
|
||||||
|
|
||||||
|
|
||||||
|
def rgb_to_256(rgb):
|
||||||
|
"""Find the closest ANSI 256 approximation to the given RGB value.
|
||||||
|
Thanks to Micah Elliott (http://MicahElliott.com) for colortrans.py
|
||||||
|
"""
|
||||||
|
rgb = rgb.lstrip('#')
|
||||||
|
if len(rgb) == 0:
|
||||||
|
return '0', '000000'
|
||||||
|
incs = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
|
||||||
|
# Break 6-char RGB code into 3 integer vals.
|
||||||
|
parts = rgb_to_ints(rgb)
|
||||||
|
res = []
|
||||||
|
for part in parts:
|
||||||
|
i = 0
|
||||||
|
while i < len(incs)-1:
|
||||||
|
s, b = incs[i], incs[i+1] # smaller, bigger
|
||||||
|
if s <= part <= b:
|
||||||
|
s1 = abs(s - part)
|
||||||
|
b1 = abs(b - part)
|
||||||
|
if s1 < b1: closest = s
|
||||||
|
else: closest = b
|
||||||
|
res.append(closest)
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
res = ''.join([('%02.x' % i) for i in res])
|
||||||
|
equiv = RGB_256[res]
|
||||||
|
return equiv, res
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_STYLE = {
|
DEFAULT_STYLE = {
|
||||||
# Reset
|
# Reset
|
||||||
|
|
|
@ -79,6 +79,7 @@ DEFAULT_ENSURERS = {
|
||||||
re.compile('\w*PATH$'): (is_env_path, str_to_env_path, env_path_to_str),
|
re.compile('\w*PATH$'): (is_env_path, str_to_env_path, env_path_to_str),
|
||||||
'PATHEXT': (is_env_path, str_to_env_path, env_path_to_str),
|
'PATHEXT': (is_env_path, str_to_env_path, env_path_to_str),
|
||||||
'RAISE_SUBPROC_ERROR': (is_bool, to_bool, bool_to_str),
|
'RAISE_SUBPROC_ERROR': (is_bool, to_bool, bool_to_str),
|
||||||
|
'RIGHT_PROMPT': (is_string, ensure_string, ensure_string),
|
||||||
'TEEPTY_PIPE_DELAY': (is_float, float, str),
|
'TEEPTY_PIPE_DELAY': (is_float, float, str),
|
||||||
'XONSHRC': (is_env_path, str_to_env_path, env_path_to_str),
|
'XONSHRC': (is_env_path, str_to_env_path, env_path_to_str),
|
||||||
'XONSH_COLOR_STYLE': (is_string, ensure_string, ensure_string),
|
'XONSH_COLOR_STYLE': (is_string, ensure_string, ensure_string),
|
||||||
|
@ -181,6 +182,7 @@ DEFAULT_VALUES = {
|
||||||
'PUSHD_MINUS': False,
|
'PUSHD_MINUS': False,
|
||||||
'PUSHD_SILENT': False,
|
'PUSHD_SILENT': False,
|
||||||
'RAISE_SUBPROC_ERROR': False,
|
'RAISE_SUBPROC_ERROR': False,
|
||||||
|
'RIGHT_PROMPT': '',
|
||||||
'SHELL_TYPE': 'best',
|
'SHELL_TYPE': 'best',
|
||||||
'SUGGEST_COMMANDS': True,
|
'SUGGEST_COMMANDS': True,
|
||||||
'SUGGEST_MAX_NUM': 5,
|
'SUGGEST_MAX_NUM': 5,
|
||||||
|
@ -349,6 +351,10 @@ DEFAULT_DOCS = {
|
||||||
'This is most useful in xonsh scripts or modules where failures '
|
'This is most useful in xonsh scripts or modules where failures '
|
||||||
'should cause an end to execution. This is less useful at a terminal.'
|
'should cause an end to execution. This is less useful at a terminal.'
|
||||||
'The error that is raised is a subprocess.CalledProcessError.'),
|
'The error that is raised is a subprocess.CalledProcessError.'),
|
||||||
|
'RIGHT_PROMPT': VarDocs('Template string for right-aligned text '
|
||||||
|
'at the prompt. This may be parameterized in the same way as '
|
||||||
|
'the $PROMPT variable. Currently, this is only available in the '
|
||||||
|
'prompt-toolkit shell.'),
|
||||||
'SHELL_TYPE': VarDocs(
|
'SHELL_TYPE': VarDocs(
|
||||||
'Which shell is used. Currently two base shell types are supported:\n\n'
|
'Which shell is used. Currently two base shell types are supported:\n\n'
|
||||||
" - 'readline' that is backed by Python's readline module\n"
|
" - 'readline' that is backed by Python's readline module\n"
|
||||||
|
|
63
xonsh/mplhooks.py
Normal file
63
xonsh/mplhooks.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""Matplotlib hooks, for what its worth."""
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from xonsh.tools import print_color
|
||||||
|
|
||||||
|
def figure_to_rgb_array(fig, width, height):
|
||||||
|
"""Converts figure to a numpy array of rgb values
|
||||||
|
|
||||||
|
Forked from http://www.icare.univ-lille1.fr/wiki/index.php/How_to_convert_a_matplotlib_figure_to_a_numpy_array_or_a_PIL_image
|
||||||
|
"""
|
||||||
|
w, h = fig.canvas.get_width_height()
|
||||||
|
dpi = fig.get_dpi()
|
||||||
|
fig.set_size_inches(width/dpi, height/dpi, forward=True)
|
||||||
|
width, height = fig.canvas.get_width_height()
|
||||||
|
ax = fig.gca()
|
||||||
|
ax.set_xticklabels([])
|
||||||
|
ax.set_yticklabels([])
|
||||||
|
fig.set_tight_layout(True)
|
||||||
|
fig.set_frameon(False)
|
||||||
|
fig.set_facecolor('w')
|
||||||
|
font_size = matplotlib.rcParams['font.size']
|
||||||
|
matplotlib.rcParams.update({'font.size': 1})
|
||||||
|
|
||||||
|
# Draw the renderer and get the RGB buffer from the figure
|
||||||
|
fig.canvas.draw()
|
||||||
|
buf = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8)
|
||||||
|
buf.shape = (height, width, 3)
|
||||||
|
|
||||||
|
# clean up and return
|
||||||
|
matplotlib.rcParams.update({'font.size': font_size})
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
|
def buf_to_color_str(buf):
|
||||||
|
"""Converts an RGB array to a xonsh color string."""
|
||||||
|
space = ' '
|
||||||
|
pix = '{{bg#{0:02x}{1:02x}{2:02x}}} '
|
||||||
|
pixels = []
|
||||||
|
for h in range(buf.shape[0]):
|
||||||
|
last = None
|
||||||
|
for w in range(buf.shape[1]):
|
||||||
|
rgb = buf[h,w]
|
||||||
|
if last is not None and (last == rgb).all():
|
||||||
|
pixels.append(space)
|
||||||
|
else:
|
||||||
|
pixels.append(pix.format(*rgb))
|
||||||
|
last = rgb
|
||||||
|
pixels.append('{NO_COLOR}\n')
|
||||||
|
pixels[-1] = pixels[-1].rstrip()
|
||||||
|
return ''.join(pixels)
|
||||||
|
|
||||||
|
|
||||||
|
def show():
|
||||||
|
fig = plt.gcf()
|
||||||
|
w, h = shutil.get_terminal_size()
|
||||||
|
h -= 1 # leave space for next prompt
|
||||||
|
buf = figure_to_rgb_array(fig, w, h)
|
||||||
|
s = buf_to_color_str(buf)
|
||||||
|
print_color(s)
|
|
@ -58,11 +58,16 @@ class PromptToolkitShell(BaseShell):
|
||||||
multicolumn = (completions_display == 'multi')
|
multicolumn = (completions_display == 'multi')
|
||||||
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
||||||
completer = None if completions_display == 'none' else self.pt_completer
|
completer = None if completions_display == 'none' else self.pt_completer
|
||||||
|
prompt_tokens = self.prompt_tokens(None)
|
||||||
|
get_prompt_tokens = lambda cli: prompt_tokens
|
||||||
|
rprompt_tokens = self.rprompt_tokens(None)
|
||||||
|
get_rprompt_tokens = lambda cli: rprompt_tokens
|
||||||
with self.prompter:
|
with self.prompter:
|
||||||
line = self.prompter.prompt(
|
line = self.prompter.prompt(
|
||||||
mouse_support=mouse_support,
|
mouse_support=mouse_support,
|
||||||
auto_suggest=auto_suggest,
|
auto_suggest=auto_suggest,
|
||||||
get_prompt_tokens=self.prompt_tokens,
|
get_prompt_tokens=get_prompt_tokens,
|
||||||
|
get_rprompt_tokens=get_rprompt_tokens,
|
||||||
style=PygmentsStyle(xonsh_style_proxy(self.styler)),
|
style=PygmentsStyle(xonsh_style_proxy(self.styler)),
|
||||||
completer=completer,
|
completer=completer,
|
||||||
lexer=PygmentsLexer(XonshLexer),
|
lexer=PygmentsLexer(XonshLexer),
|
||||||
|
@ -127,6 +132,20 @@ class PromptToolkitShell(BaseShell):
|
||||||
self.settitle()
|
self.settitle()
|
||||||
return toks
|
return toks
|
||||||
|
|
||||||
|
def rprompt_tokens(self, cli):
|
||||||
|
"""Returns a list of (token, str) tuples for the current right
|
||||||
|
prompt.
|
||||||
|
"""
|
||||||
|
p = builtins.__xonsh_env__.get('RIGHT_PROMPT')
|
||||||
|
if len(p) == 0:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
p = partial_format_prompt(p)
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
print_exception()
|
||||||
|
toks = partial_color_tokenize(p)
|
||||||
|
return toks
|
||||||
|
|
||||||
def format_color(self, string, **kwargs):
|
def format_color(self, string, **kwargs):
|
||||||
"""Formats a color string using Pygments. This, therefore, returns
|
"""Formats a color string using Pygments. This, therefore, returns
|
||||||
a list of (Token, str) tuples.
|
a list of (Token, str) tuples.
|
||||||
|
|
|
@ -4,6 +4,8 @@ from prompt_toolkit.utils import DummyContext
|
||||||
from prompt_toolkit.shortcuts import (create_prompt_application,
|
from prompt_toolkit.shortcuts import (create_prompt_application,
|
||||||
create_eventloop, create_asyncio_eventloop, create_output)
|
create_eventloop, create_asyncio_eventloop, create_output)
|
||||||
|
|
||||||
|
from xonsh.shell import prompt_toolkit_version_info
|
||||||
|
|
||||||
class Prompter(object):
|
class Prompter(object):
|
||||||
|
|
||||||
def __init__(self, cli=None, *args, **kwargs):
|
def __init__(self, cli=None, *args, **kwargs):
|
||||||
|
@ -18,6 +20,7 @@ class Prompter(object):
|
||||||
will be created when the prompt() method is called.
|
will be created when the prompt() method is called.
|
||||||
"""
|
"""
|
||||||
self.cli = cli
|
self.cli = cli
|
||||||
|
self.major_minor = prompt_toolkit_version_info()[:2]
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.reset()
|
self.reset()
|
||||||
|
@ -61,6 +64,8 @@ class Prompter(object):
|
||||||
|
|
||||||
# Create CommandLineInterface.
|
# Create CommandLineInterface.
|
||||||
if self.cli is None:
|
if self.cli is None:
|
||||||
|
if self.major_minor <= (0, 57):
|
||||||
|
kwargs.pop('get_rprompt_tokens', None)
|
||||||
cli = CommandLineInterface(
|
cli = CommandLineInterface(
|
||||||
application=create_prompt_application(message, **kwargs),
|
application=create_prompt_application(message, **kwargs),
|
||||||
eventloop=eventloop,
|
eventloop=eventloop,
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Hooks for pygments syntax highlighting."""
|
"""Hooks for pygments syntax highlighting."""
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
|
import builtins
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
|
||||||
from pygments.lexer import inherit, bygroups, using, this
|
from pygments.lexer import inherit, bygroups, using, this
|
||||||
from pygments.token import (Keyword, Name, Comment, String, Error, Number,
|
from pygments.token import (Keyword, Name, Comment, String, Error, Number,
|
||||||
|
@ -82,26 +85,131 @@ XonshSubprocLexer.tokens['root'] = [
|
||||||
|
|
||||||
Color = Token.Color # alias to new color token namespace
|
Color = Token.Color # alias to new color token namespace
|
||||||
|
|
||||||
|
RE_BACKGROUND = re.compile('(BG#|BGHEX|BACKGROUND)')
|
||||||
|
|
||||||
|
def norm_name(name):
|
||||||
|
"""Normalizes a color name."""
|
||||||
|
return name.replace('#', 'HEX').replace('BGHEX', 'BACKGROUND_HEX')
|
||||||
|
|
||||||
|
def color_by_name(name, fg=None, bg=None):
|
||||||
|
"""Converts a color name to a color token, foreground name,
|
||||||
|
and background name. Will take into consideration current foreground
|
||||||
|
and background colors, if provided.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Color name.
|
||||||
|
fg : str, optional
|
||||||
|
Foreground color name.
|
||||||
|
bg : str, optional
|
||||||
|
Background color name.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tok : Token
|
||||||
|
Pygments Token.Color subclass
|
||||||
|
fg : str or None
|
||||||
|
New computed foreground color name.
|
||||||
|
bg : str or None
|
||||||
|
New computed background color name.
|
||||||
|
"""
|
||||||
|
name = name.upper()
|
||||||
|
if name == 'NO_COLOR':
|
||||||
|
return Color.NO_COLOR, None, None
|
||||||
|
m = RE_BACKGROUND.search(name)
|
||||||
|
if m is None: # must be foreground color
|
||||||
|
fg = norm_name(name)
|
||||||
|
else:
|
||||||
|
bg = norm_name(name)
|
||||||
|
# assmble token
|
||||||
|
if fg is None and bg is None:
|
||||||
|
tokname = 'NO_COLOR'
|
||||||
|
elif fg is None:
|
||||||
|
tokname = bg
|
||||||
|
elif bg is None:
|
||||||
|
tokname = fg
|
||||||
|
else:
|
||||||
|
tokname = fg + '__' + bg
|
||||||
|
tok = getattr(Color, tokname)
|
||||||
|
return tok, fg, bg
|
||||||
|
|
||||||
|
|
||||||
|
def code_by_name(name, styles):
|
||||||
|
"""Converts a token name into a pygments-style color code.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Color token name.
|
||||||
|
styles : Mapping
|
||||||
|
Mapping for looking up non-hex colors
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
code : str
|
||||||
|
Pygments style color code.
|
||||||
|
"""
|
||||||
|
fg, _, bg = name.lower().partition('__')
|
||||||
|
if fg.startswith('background_'):
|
||||||
|
fg, bg = bg, fg
|
||||||
|
codes = []
|
||||||
|
# foreground color
|
||||||
|
if len(fg) == 0:
|
||||||
|
pass
|
||||||
|
elif 'hex' in fg:
|
||||||
|
for p in fg.split('_'):
|
||||||
|
codes.append('#'+p[3:] if p.startswith('hex') else p)
|
||||||
|
else:
|
||||||
|
fgtok = getattr(Color, fg.upper())
|
||||||
|
if fgtok in styles:
|
||||||
|
codes.append(styles[fgtok])
|
||||||
|
else:
|
||||||
|
codes += fg.split('_')
|
||||||
|
# background color
|
||||||
|
if len(bg) == 0:
|
||||||
|
pass
|
||||||
|
elif bg.startswith('background_hex'):
|
||||||
|
codes.append('bg:#'+bg[14:])
|
||||||
|
else:
|
||||||
|
bgtok = getattr(Color, bg.upper())
|
||||||
|
if bgtok in styles:
|
||||||
|
codes.append(styles[bgtok])
|
||||||
|
else:
|
||||||
|
codes.append(bg.replace('background_', 'bg:'))
|
||||||
|
code = ' '.join(codes)
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
def partial_color_tokenize(template):
|
def partial_color_tokenize(template):
|
||||||
"""Toeknizes a template string containing colors. Will return a list
|
"""Toeknizes a template string containing colors. Will return a list
|
||||||
of tuples mapping the token to the string which has that color.
|
of tuples mapping the token to the string which has that color.
|
||||||
These sub-strings maybe templates themselves.
|
These sub-strings maybe templates themselves.
|
||||||
"""
|
"""
|
||||||
formatter = string.Formatter()
|
formatter = string.Formatter()
|
||||||
|
if hasattr(builtins, '__xonsh_shell__'):
|
||||||
|
styles = __xonsh_shell__.shell.styler.styles
|
||||||
|
else:
|
||||||
|
styles = None
|
||||||
bopen = '{'
|
bopen = '{'
|
||||||
bclose = '}'
|
bclose = '}'
|
||||||
colon = ':'
|
colon = ':'
|
||||||
expl = '!'
|
expl = '!'
|
||||||
color = Color.NO_COLOR
|
color = Color.NO_COLOR
|
||||||
|
fg = bg = None
|
||||||
value = ''
|
value = ''
|
||||||
toks = []
|
toks = []
|
||||||
for literal, field, spec, conv in formatter.parse(template):
|
for literal, field, spec, conv in formatter.parse(template):
|
||||||
if field in KNOWN_COLORS:
|
if field is None:
|
||||||
value += literal
|
value += literal
|
||||||
next_color = getattr(Color, field)
|
elif field in KNOWN_COLORS or '#' in field:
|
||||||
|
value += literal
|
||||||
|
next_color, fg, bg = color_by_name(field, fg, bg)
|
||||||
if next_color is not color:
|
if next_color is not color:
|
||||||
if len(value) > 0:
|
if len(value) > 0:
|
||||||
toks.append((color, value))
|
toks.append((color, value))
|
||||||
|
if styles is not None:
|
||||||
|
styles[color] # ensure color is available
|
||||||
color = next_color
|
color = next_color
|
||||||
value = ''
|
value = ''
|
||||||
elif field is not None:
|
elif field is not None:
|
||||||
|
@ -117,9 +225,49 @@ def partial_color_tokenize(template):
|
||||||
else:
|
else:
|
||||||
value += literal
|
value += literal
|
||||||
toks.append((color, value))
|
toks.append((color, value))
|
||||||
|
if styles is not None:
|
||||||
|
styles[color] # ensure color is available
|
||||||
return toks
|
return toks
|
||||||
|
|
||||||
|
|
||||||
|
class CompoundColorMap(MutableMapping):
|
||||||
|
"""Looks up color tokes by name, potentailly generating the value
|
||||||
|
from the lookup.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, styles, *args, **kwargs):
|
||||||
|
self.styles = styles
|
||||||
|
self.colors = dict(*args, **kwargs)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if key in self.colors:
|
||||||
|
return self.colors[key]
|
||||||
|
if key in self.styles:
|
||||||
|
value = self.styles[key]
|
||||||
|
self[key] = value
|
||||||
|
return value
|
||||||
|
if key is Color:
|
||||||
|
raise KeyError
|
||||||
|
pre, _, name = str(key).rpartition('.')
|
||||||
|
if pre != 'Token.Color':
|
||||||
|
raise KeyError
|
||||||
|
value = code_by_name(name, self.styles)
|
||||||
|
self[key] = value
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self.colors[key] = value
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
del self.colors[key]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield from self.colors.keys()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.colors)
|
||||||
|
|
||||||
|
|
||||||
class XonshStyle(Style):
|
class XonshStyle(Style):
|
||||||
"""A xonsh pygments style that will dispatch to the correct color map
|
"""A xonsh pygments style that will dispatch to the correct color map
|
||||||
by using a ChainMap. The style_name property may be used to reset
|
by using a ChainMap. The style_name property may be used to reset
|
||||||
|
@ -156,7 +304,8 @@ class XonshStyle(Style):
|
||||||
smap = get_style_by_name(value)().styles
|
smap = get_style_by_name(value)().styles
|
||||||
except (ImportError, pygments.util.ClassNotFound):
|
except (ImportError, pygments.util.ClassNotFound):
|
||||||
smap = XONSH_BASE_STYLE
|
smap = XONSH_BASE_STYLE
|
||||||
self.styles = ChainMap(self.trap, cmap, PTK_STYLE, smap)
|
compound = CompoundColorMap(ChainMap(self.trap, cmap, PTK_STYLE, smap))
|
||||||
|
self.styles = ChainMap(self.trap, cmap, PTK_STYLE, smap, compound)
|
||||||
self._style_name = value
|
self._style_name = value
|
||||||
|
|
||||||
@style_name.deleter
|
@style_name.deleter
|
||||||
|
|
|
@ -33,6 +33,12 @@ def prompt_toolkit_version():
|
||||||
return getattr(prompt_toolkit, '__version__', '<0.57')
|
return getattr(prompt_toolkit, '__version__', '<0.57')
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_toolkit_version_info():
|
||||||
|
"""Gets the prompt toolkit version info tuple."""
|
||||||
|
v = prompt_toolkit_version().strip('<>+-=.')
|
||||||
|
return tuple(map(int, v.split('.')))
|
||||||
|
|
||||||
|
|
||||||
def best_shell_type():
|
def best_shell_type():
|
||||||
"""Gets the best shell type that is available"""
|
"""Gets the best shell type that is available"""
|
||||||
if ON_WINDOWS or is_prompt_toolkit_available():
|
if ON_WINDOWS or is_prompt_toolkit_available():
|
||||||
|
|
Loading…
Add table
Reference in a new issue