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.
|
||||
* ``??`` will syntax highlight source code 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:**
|
||||
|
||||
|
|
|
@ -875,6 +875,7 @@ keyword arguments, which will be replaced automatically:
|
|||
snail@home:~ >> # so does that!
|
||||
|
||||
By default, the following variables are available for use:
|
||||
|
||||
* ``user``: The username of the current user
|
||||
* ``hostname``: The name of the host computer
|
||||
* ``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}``
|
||||
or ``{BOLD_BLUE}``. Colors have the form shown below:
|
||||
|
||||
* ``(QUALIFIER\_)COLORNAME``: Inserts an ANSI color code
|
||||
* ``COLORNAME`` can be any of: ``BLACK``, ``RED``, ``GREEN``, ``YELLOW``,
|
||||
``BLUE``, ``PURPLE``, ``CYAN``, or ``WHITE``
|
||||
* ``QUALIFIER`` is optional and can be any of: ``BOLD``, ``UNDERLINE``,
|
||||
``BACKGROUND``, ``INTENSE``, ``BOLD_INTENSE``, or
|
||||
``BACKGROUND_INTENSE``
|
||||
* ``NO_COLOR``: Resets any previously used color codes
|
||||
* ``NO_COLOR``: Resets any previously used color codes
|
||||
* ``COLORNAME``: Inserts a color code for the following basic colors,
|
||||
which come in regular (dark) and intense (light) forms:
|
||||
|
||||
- ``BLACK`` or ``INTENSE_BLACK``
|
||||
- ``RED`` or ``INTENSE_RED``
|
||||
- ``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
|
||||
``FORMATTER_DICT`` environment variable. The values in this dictionary
|
||||
|
|
|
@ -6,6 +6,7 @@ import glob
|
|||
import builtins
|
||||
import platform
|
||||
import subprocess
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
@ -23,10 +24,13 @@ ON_MAC = (platform.system() == 'Darwin')
|
|||
def sp(cmd):
|
||||
return subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
||||
class DummyStyler():
|
||||
styles = defaultdict(None.__class__)
|
||||
|
||||
class DummyBaseShell(BaseShell):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
self.styler = DummyStyler()
|
||||
|
||||
|
||||
class DummyShell:
|
||||
|
|
|
@ -75,7 +75,7 @@ def source_foreign(args, stdin=None):
|
|||
"""Sources a file written in a foreign shell language."""
|
||||
parser = _ensure_source_foreign_parser()
|
||||
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
|
||||
elif os.path.isfile(ns.files_or_code[0]):
|
||||
# we have filename to source
|
||||
|
@ -191,6 +191,11 @@ def vox(args, stdin=None):
|
|||
vox = Vox()
|
||||
return vox(args, stdin=stdin)
|
||||
|
||||
def mpl(args, stdin=None):
|
||||
"""Hooks to matplotlib"""
|
||||
from xonsh.mplhooks import show
|
||||
show()
|
||||
|
||||
|
||||
DEFAULT_ALIASES = {
|
||||
'cd': cd,
|
||||
|
@ -212,6 +217,7 @@ DEFAULT_ALIASES = {
|
|||
'replay': replay_main,
|
||||
'!!': bang_bang,
|
||||
'!n': bang_n,
|
||||
'mpl': mpl,
|
||||
'trace': trace,
|
||||
'timeit': timeit_alias,
|
||||
'xonfig': xonfig,
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
"""Tools for helping with ANSI color codes."""
|
||||
import re
|
||||
import string
|
||||
from warnings import warn
|
||||
|
||||
RE_BACKGROUND = re.compile('(bg|bg#|bghex|background)')
|
||||
|
||||
def partial_color_format(template, style='default', cmap=None, hide=False):
|
||||
"""Formats a template string but only with respect to the colors.
|
||||
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 = []
|
||||
for literal, field, spec, conv in formatter.parse(template):
|
||||
toks.append(literal)
|
||||
if field in cmap:
|
||||
if field is None:
|
||||
pass
|
||||
elif field in cmap:
|
||||
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:
|
||||
toks.append(bopen)
|
||||
toks.append(field)
|
||||
|
@ -56,6 +74,294 @@ def partial_color_format(template, style='default', cmap=None, hide=False):
|
|||
toks.append(bclose)
|
||||
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 = {
|
||||
# Reset
|
||||
|
|
|
@ -79,6 +79,7 @@ DEFAULT_ENSURERS = {
|
|||
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),
|
||||
'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),
|
||||
'XONSHRC': (is_env_path, str_to_env_path, env_path_to_str),
|
||||
'XONSH_COLOR_STYLE': (is_string, ensure_string, ensure_string),
|
||||
|
@ -181,6 +182,7 @@ DEFAULT_VALUES = {
|
|||
'PUSHD_MINUS': False,
|
||||
'PUSHD_SILENT': False,
|
||||
'RAISE_SUBPROC_ERROR': False,
|
||||
'RIGHT_PROMPT': '',
|
||||
'SHELL_TYPE': 'best',
|
||||
'SUGGEST_COMMANDS': True,
|
||||
'SUGGEST_MAX_NUM': 5,
|
||||
|
@ -349,6 +351,10 @@ DEFAULT_DOCS = {
|
|||
'This is most useful in xonsh scripts or modules where failures '
|
||||
'should cause an end to execution. This is less useful at a terminal.'
|
||||
'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(
|
||||
'Which shell is used. Currently two base shell types are supported:\n\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')
|
||||
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
||||
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:
|
||||
line = self.prompter.prompt(
|
||||
mouse_support=mouse_support,
|
||||
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)),
|
||||
completer=completer,
|
||||
lexer=PygmentsLexer(XonshLexer),
|
||||
|
@ -127,6 +132,20 @@ class PromptToolkitShell(BaseShell):
|
|||
self.settitle()
|
||||
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):
|
||||
"""Formats a color string using Pygments. This, therefore, returns
|
||||
a list of (Token, str) tuples.
|
||||
|
|
|
@ -4,6 +4,8 @@ from prompt_toolkit.utils import DummyContext
|
|||
from prompt_toolkit.shortcuts import (create_prompt_application,
|
||||
create_eventloop, create_asyncio_eventloop, create_output)
|
||||
|
||||
from xonsh.shell import prompt_toolkit_version_info
|
||||
|
||||
class Prompter(object):
|
||||
|
||||
def __init__(self, cli=None, *args, **kwargs):
|
||||
|
@ -18,6 +20,7 @@ class Prompter(object):
|
|||
will be created when the prompt() method is called.
|
||||
"""
|
||||
self.cli = cli
|
||||
self.major_minor = prompt_toolkit_version_info()[:2]
|
||||
|
||||
def __enter__(self):
|
||||
self.reset()
|
||||
|
@ -61,6 +64,8 @@ class Prompter(object):
|
|||
|
||||
# Create CommandLineInterface.
|
||||
if self.cli is None:
|
||||
if self.major_minor <= (0, 57):
|
||||
kwargs.pop('get_rprompt_tokens', None)
|
||||
cli = CommandLineInterface(
|
||||
application=create_prompt_application(message, **kwargs),
|
||||
eventloop=eventloop,
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Hooks for pygments syntax highlighting."""
|
||||
import re
|
||||
import string
|
||||
import builtins
|
||||
from warnings import warn
|
||||
from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
|
||||
from pygments.lexer import inherit, bygroups, using, this
|
||||
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
|
||||
|
||||
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):
|
||||
"""Toeknizes a template string containing colors. Will return a list
|
||||
of tuples mapping the token to the string which has that color.
|
||||
These sub-strings maybe templates themselves.
|
||||
"""
|
||||
formatter = string.Formatter()
|
||||
if hasattr(builtins, '__xonsh_shell__'):
|
||||
styles = __xonsh_shell__.shell.styler.styles
|
||||
else:
|
||||
styles = None
|
||||
bopen = '{'
|
||||
bclose = '}'
|
||||
colon = ':'
|
||||
expl = '!'
|
||||
color = Color.NO_COLOR
|
||||
fg = bg = None
|
||||
value = ''
|
||||
toks = []
|
||||
for literal, field, spec, conv in formatter.parse(template):
|
||||
if field in KNOWN_COLORS:
|
||||
if field is None:
|
||||
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 len(value) > 0:
|
||||
toks.append((color, value))
|
||||
if styles is not None:
|
||||
styles[color] # ensure color is available
|
||||
color = next_color
|
||||
value = ''
|
||||
elif field is not None:
|
||||
|
@ -117,9 +225,49 @@ def partial_color_tokenize(template):
|
|||
else:
|
||||
value += literal
|
||||
toks.append((color, value))
|
||||
if styles is not None:
|
||||
styles[color] # ensure color is available
|
||||
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):
|
||||
"""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
|
||||
|
@ -156,7 +304,8 @@ class XonshStyle(Style):
|
|||
smap = get_style_by_name(value)().styles
|
||||
except (ImportError, pygments.util.ClassNotFound):
|
||||
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
|
||||
|
||||
@style_name.deleter
|
||||
|
|
|
@ -33,6 +33,12 @@ def prompt_toolkit_version():
|
|||
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():
|
||||
"""Gets the best shell type that is available"""
|
||||
if ON_WINDOWS or is_prompt_toolkit_available():
|
||||
|
|
Loading…
Add table
Reference in a new issue