mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
commit
b330ff277d
15 changed files with 5070 additions and 374 deletions
|
@ -8,14 +8,38 @@ Current Developments
|
|||
|
||||
* Added new valid ``$SHELL_TYPE`` called ``'best'``. This selects the best value
|
||||
for the concrete shell type based on the availability on the user's machine.
|
||||
* New environment variable ``$XONSH_COLOR_STYLE`` will set the color mapping
|
||||
for all of xonsh.
|
||||
* New ``XonshStyle`` pygments style will determine the approriate color
|
||||
mapping based on ``$XONSH_COLOR_STYLE``. The associated ``xonsh_style_proxy()``
|
||||
is intended for wrapping ``XonshStyle`` when actually being used by
|
||||
pygments.
|
||||
* The functions ``print_color()`` and ``format_color()`` found in ``xonsh.tools``
|
||||
dispatch to the approriate shell color handling and may be used from
|
||||
anywhere.
|
||||
* ``xonsh.tools.HAVE_PYGMENTS`` flag now denotes if pygments is installed and
|
||||
available on the users system.
|
||||
* The ``ansi_colors`` module is now availble for handling ANSI color codes.
|
||||
* ``?`` 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.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* Updated ``$SHELL_TYPE`` default to ``'best'``.
|
||||
* Shell classes are now responsible for implementing their own color
|
||||
formatting and printing.
|
||||
* Prompt coloring, history diffing, and tracing uses new color handling
|
||||
capabilities.
|
||||
* New ``Token.Color`` token for xonsh color names, e.g. we now use
|
||||
``Token.Color.RED`` rather than ``Token.RED``.
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
**Removed:**
|
||||
|
||||
* The ``xonsh.tools.TERM_COLORS`` mapping has been axed, along with all
|
||||
references to it.
|
||||
|
||||
**Fixed:**
|
||||
|
||||
|
|
|
@ -76,22 +76,28 @@ def test_repath_home_var_brace():
|
|||
assert_equal(exp, obs[0])
|
||||
|
||||
def test_helper_int():
|
||||
helper(int, 'int')
|
||||
with mock_xonsh_env({}):
|
||||
helper(int, 'int')
|
||||
|
||||
def test_helper_helper():
|
||||
helper(helper, 'helper')
|
||||
with mock_xonsh_env({}):
|
||||
helper(helper, 'helper')
|
||||
|
||||
def test_helper_env():
|
||||
helper(Env, 'Env')
|
||||
with mock_xonsh_env({}):
|
||||
helper(Env, 'Env')
|
||||
|
||||
def test_superhelper_int():
|
||||
superhelper(int, 'int')
|
||||
with mock_xonsh_env({}):
|
||||
superhelper(int, 'int')
|
||||
|
||||
def test_superhelper_helper():
|
||||
superhelper(helper, 'helper')
|
||||
with mock_xonsh_env({}):
|
||||
superhelper(helper, 'helper')
|
||||
|
||||
def test_superhelper_env():
|
||||
superhelper(Env, 'Env')
|
||||
with mock_xonsh_env({}):
|
||||
superhelper(Env, 'Env')
|
||||
|
||||
def test_ensure_list_of_strs():
|
||||
cases = [(['yo'], 'yo'), (['yo'], ['yo']), (['42'], 42), (['42'], [42])]
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Tests some tools function for prompt_toolkit integration."""
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
import nose
|
||||
from nose.tools import assert_equal
|
||||
|
||||
import builtins
|
||||
from xonsh.tools import format_prompt_for_prompt_toolkit
|
||||
from xonsh.tools import TERM_COLORS
|
||||
from xonsh.environ import format_prompt, Env
|
||||
|
||||
builtins.__xonsh_env__ = Env()
|
||||
builtins.__xonsh_env__['PROMPT_TOOLKIT_COLORS'] = {'WHITE': '#f0f0f0'}
|
||||
|
||||
def test_format_prompt_for_prompt_toolkit():
|
||||
templ = ('>>> {BOLD_INTENSE_BLUE}~/xonsh {WHITE} {BACKGROUND_RED} {INTENSE_RED}(main){NO_COLOR}')
|
||||
prompt = format_prompt(templ, TERM_COLORS)
|
||||
token_names, color_styles, strings = format_prompt_for_prompt_toolkit(prompt)
|
||||
assert_equal(token_names, ['NO_COLOR', 'BOLD_INTENSE_BLUE', 'WHITE', 'BACKGROUND_RED', 'INTENSE_RED', 'NO_COLOR'])
|
||||
assert_equal(color_styles, ['noinherit', 'bold #0000d2', '#f0f0f0', 'bg:#800000', 'bg:#800000 #ff1010', 'noinherit'])
|
||||
assert_equal(strings, ['>>> ', '~/xonsh ', ' ', ' ', '(main)', ''])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
nose.runmodule()
|
|
@ -11,6 +11,7 @@ from contextlib import contextmanager
|
|||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from xonsh.built_ins import ensure_list_of_strs
|
||||
from xonsh.base_shell import BaseShell
|
||||
|
||||
|
||||
VER_3_4 = (3, 4)
|
||||
|
@ -22,10 +23,25 @@ ON_MAC = (platform.system() == 'Darwin')
|
|||
def sp(cmd):
|
||||
return subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
||||
class DummyBaseShell(BaseShell):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class DummyShell:
|
||||
def settitle():
|
||||
pass
|
||||
|
||||
_shell = None
|
||||
|
||||
@property
|
||||
def shell(self):
|
||||
if self._shell is None:
|
||||
self._shell = DummyBaseShell()
|
||||
return self._shell
|
||||
|
||||
|
||||
@contextmanager
|
||||
def mock_xonsh_env(xenv):
|
||||
builtins.__xonsh_env__ = xenv
|
||||
|
|
2260
xonsh/ansi_colors.py
Normal file
2260
xonsh/ansi_colors.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -7,9 +7,11 @@ import time
|
|||
import builtins
|
||||
|
||||
from xonsh.tools import XonshError, escape_windows_title_string, ON_WINDOWS, \
|
||||
print_exception, format_color
|
||||
print_exception, HAVE_PYGMENTS
|
||||
from xonsh.completer import Completer
|
||||
from xonsh.environ import multiline_prompt, format_prompt
|
||||
if HAVE_PYGMENTS:
|
||||
from xonsh.pyghooks import XonshStyle
|
||||
|
||||
|
||||
class _TeeOut(object):
|
||||
|
@ -30,7 +32,7 @@ class _TeeOut(object):
|
|||
|
||||
def write(self, data):
|
||||
"""Writes data to the original stdout and the buffer."""
|
||||
data = data.replace('\001', '').replace('\002', '')
|
||||
#data = data.replace('\001', '').replace('\002', '')
|
||||
self.stdout.write(data)
|
||||
self.buffer.write(data)
|
||||
|
||||
|
@ -63,7 +65,7 @@ class _TeeErr(object):
|
|||
|
||||
def write(self, data):
|
||||
"""Writes data to the original stderr and the buffer."""
|
||||
data = data.replace('\001', '').replace('\002', '')
|
||||
#data = data.replace('\001', '').replace('\002', '')
|
||||
self.stderr.write(data)
|
||||
self.buffer.write(data)
|
||||
|
||||
|
@ -113,6 +115,11 @@ class BaseShell(object):
|
|||
self.buffer = []
|
||||
self.need_more_lines = False
|
||||
self.mlprompt = None
|
||||
if HAVE_PYGMENTS:
|
||||
env = builtins.__xonsh_env__
|
||||
self.styler = XonshStyle(env.get('XONSH_COLOR_STYLE'))
|
||||
else:
|
||||
self.styler = None
|
||||
|
||||
def emptyline(self):
|
||||
"""Called when an empty line has been entered."""
|
||||
|
@ -250,8 +257,16 @@ class BaseShell(object):
|
|||
hist.append(info)
|
||||
hist.last_cmd_rtn = hist.last_cmd_out = None
|
||||
|
||||
def print_color(self, string, **kwargs):
|
||||
"""Prints a string in color. This base implmentation uses ANSI
|
||||
color codes.
|
||||
def format_color(self, string, **kwargs):
|
||||
"""Formats the colors in a string. This base implmentation does not
|
||||
actually do any coloring, but just returns the string directly.
|
||||
"""
|
||||
print(format_color(string), **kwargs)
|
||||
return string
|
||||
|
||||
def print_color(self, string, **kwargs):
|
||||
"""Prints a string in color. This base implmentation does not actually
|
||||
do any coloring, but just prints the string directly.
|
||||
"""
|
||||
if not isinstance(string, str):
|
||||
string = ''.join([x for _, x in string])
|
||||
print(string, **kwargs)
|
||||
|
|
|
@ -5,13 +5,13 @@ from itertools import zip_longest
|
|||
from difflib import SequenceMatcher
|
||||
|
||||
from xonsh import lazyjson
|
||||
from xonsh.tools import TERM_COLORS
|
||||
from xonsh.tools import print_color, format_color
|
||||
|
||||
NO_COLOR = TERM_COLORS['NO_COLOR'].replace('\001', '').replace('\002', '')
|
||||
RED = TERM_COLORS['RED'].replace('\001', '').replace('\002', '')
|
||||
GREEN = TERM_COLORS['GREEN'].replace('\001', '').replace('\002', '')
|
||||
BOLD_RED = TERM_COLORS['BOLD_RED'].replace('\001', '').replace('\002', '')
|
||||
BOLD_GREEN = TERM_COLORS['BOLD_GREEN'].replace('\001', '').replace('\002', '')
|
||||
NO_COLOR = '{NO_COLOR}'
|
||||
RED = '{RED}'
|
||||
GREEN = '{GREEN}'
|
||||
BOLD_RED = '{BOLD_RED}'
|
||||
BOLD_GREEN = '{BOLD_GREEN}'
|
||||
|
||||
# intern some strings
|
||||
REPLACE = 'replace'
|
||||
|
@ -299,7 +299,7 @@ def _create_parser(p=None):
|
|||
|
||||
def _main_action(ns, hist=None):
|
||||
hd = HistoryDiffer(ns.a, ns.b, reopen=ns.reopen, verbose=ns.verbose)
|
||||
print(hd.format())
|
||||
print_color(hd.format())
|
||||
|
||||
|
||||
def main(args=None, stdin=None):
|
||||
|
|
|
@ -17,7 +17,7 @@ from collections import MutableMapping, MutableSequence, MutableSet, namedtuple
|
|||
|
||||
from xonsh import __version__ as XONSH_VERSION
|
||||
from xonsh.tools import (
|
||||
TERM_COLORS, ON_WINDOWS, ON_MAC, ON_LINUX, ON_ARCH, IS_ROOT,
|
||||
ON_WINDOWS, ON_MAC, ON_LINUX, ON_ARCH, IS_ROOT,
|
||||
always_true, always_false, ensure_string, is_env_path, str_to_env_path,
|
||||
env_path_to_str, is_bool, to_bool, bool_to_str, is_history_tuple, to_history_tuple,
|
||||
history_tuple_to_str, is_float, string_types, is_string, DEFAULT_ENCODING,
|
||||
|
@ -81,6 +81,7 @@ DEFAULT_ENSURERS = {
|
|||
'RAISE_SUBPROC_ERROR': (is_bool, to_bool, bool_to_str),
|
||||
'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),
|
||||
'XONSH_ENCODING': (is_string, ensure_string, ensure_string),
|
||||
'XONSH_ENCODING_ERRORS': (is_string, ensure_string, ensure_string),
|
||||
'XONSH_HISTORY_SIZE': (is_history_tuple, to_history_tuple, history_tuple_to_str),
|
||||
|
@ -195,6 +196,7 @@ DEFAULT_VALUES = {
|
|||
'xonsh', 'xonshrc'),
|
||||
os.path.expanduser('~/.xonshrc')) if ON_WINDOWS
|
||||
else ('/etc/xonshrc', os.path.expanduser('~/.xonshrc'))),
|
||||
'XONSH_COLOR_STYLE': 'default',
|
||||
'XONSH_CONFIG_DIR': xonsh_config_dir,
|
||||
'XONSH_DATA_DIR': xonsh_data_dir,
|
||||
'XONSH_ENCODING': DEFAULT_ENCODING,
|
||||
|
@ -408,6 +410,9 @@ DEFAULT_DOCS = {
|
|||
'control file if there is a naming collision.', default=(
|
||||
"On Linux & Mac OSX: ('/etc/xonshrc', '~/.xonshrc')\n"
|
||||
"On Windows: ('%ALLUSERSPROFILE%\\xonsh\\xonshrc', '~/.xonshrc')")),
|
||||
'XONSH_COLOR_STYLE': VarDocs(
|
||||
'Sets the color style for xonsh colors. This is a style name, not '
|
||||
'a color map.'),
|
||||
'XONSH_CONFIG_DIR': VarDocs(
|
||||
'This is the location where xonsh configuration information is stored.',
|
||||
configurable=False, default="'$XDG_CONFIG_HOME/xonsh'"),
|
||||
|
@ -882,14 +887,14 @@ def dirty_working_directory(cwd=None):
|
|||
|
||||
def branch_color():
|
||||
"""Return red if the current branch is dirty, otherwise green"""
|
||||
return (TERM_COLORS['BOLD_INTENSE_RED'] if dirty_working_directory() else
|
||||
TERM_COLORS['BOLD_INTENSE_GREEN'])
|
||||
return ('{BOLD_INTENSE_RED}' if dirty_working_directory() else
|
||||
'{BOLD_INTENSE_GREEN}')
|
||||
|
||||
|
||||
def branch_bg_color():
|
||||
"""Return red if the current branch is dirty, otherwise green"""
|
||||
return (TERM_COLORS['BACKGROUND_RED'] if dirty_working_directory() else
|
||||
TERM_COLORS['BACKGROUND_GREEN'])
|
||||
return ('{BACKGROUND_RED}' if dirty_working_directory() else
|
||||
'{BACKGROUND_GREEN}')
|
||||
|
||||
|
||||
def _replace_home(x):
|
||||
|
@ -961,7 +966,7 @@ FORMATTER_DICT = dict(
|
|||
branch_bg_color=branch_bg_color,
|
||||
current_job=_current_job,
|
||||
env_name=env_name,
|
||||
**TERM_COLORS)
|
||||
)
|
||||
DEFAULT_VALUES['FORMATTER_DICT'] = dict(FORMATTER_DICT)
|
||||
|
||||
_FORMATTER = string.Formatter()
|
||||
|
@ -983,13 +988,18 @@ def is_template_string(template, formatter_dict=None):
|
|||
return included_names <= known_names
|
||||
|
||||
|
||||
def format_prompt(template=DEFAULT_PROMPT, formatter_dict=None):
|
||||
"""Formats a xonsh prompt template string."""
|
||||
template = template() if callable(template) else template
|
||||
def _get_fmtter(formatter_dict=None):
|
||||
if formatter_dict is None:
|
||||
fmtter = builtins.__xonsh_env__.get('FORMATTER_DICT', FORMATTER_DICT)
|
||||
else:
|
||||
fmtter = formatter_dict
|
||||
return fmtter
|
||||
|
||||
|
||||
def format_prompt(template=DEFAULT_PROMPT, formatter_dict=None):
|
||||
"""Formats a xonsh prompt template string."""
|
||||
template = template() if callable(template) else template
|
||||
fmtter = _get_fmtter(formatter_dict)
|
||||
included_names = set(i[1] for i in _FORMATTER.parse(template))
|
||||
fmt = {}
|
||||
for name in included_names:
|
||||
|
@ -1005,12 +1015,47 @@ def format_prompt(template=DEFAULT_PROMPT, formatter_dict=None):
|
|||
return template.format(**fmt)
|
||||
|
||||
|
||||
def partial_format_prompt(template=DEFAULT_PROMPT, formatter_dict=None):
|
||||
"""Formats a xonsh prompt template string."""
|
||||
template = template() if callable(template) else template
|
||||
fmtter = _get_fmtter(formatter_dict)
|
||||
bopen = '{'
|
||||
bclose = '}'
|
||||
colon = ':'
|
||||
expl = '!'
|
||||
toks = []
|
||||
for literal, field, spec, conv in _FORMATTER.parse(template):
|
||||
toks.append(literal)
|
||||
if field is None:
|
||||
continue
|
||||
elif field.startswith('$'):
|
||||
v = builtins.__xonsh_env__[name[1:]]
|
||||
v = _FORMATTER.convert_field(v, conv)
|
||||
v = _FORMATTER.format_field(v, spec)
|
||||
toks.append(v)
|
||||
continue
|
||||
elif field in fmtter:
|
||||
v = fmtter[field]
|
||||
val = v() if callable(v) else v
|
||||
val = '' if val is None else val
|
||||
toks.append(val)
|
||||
else:
|
||||
toks.append(bopen)
|
||||
toks.append(field)
|
||||
if conv is not None and len(conv) > 0:
|
||||
toks.append(expl)
|
||||
toks.append(conv)
|
||||
if spec is not None and len(spec) > 0:
|
||||
toks.append(colon)
|
||||
toks.append(spec)
|
||||
toks.append(bclose)
|
||||
return ''.join(toks)
|
||||
|
||||
|
||||
RE_HIDDEN = re.compile('\001.*?\002')
|
||||
|
||||
def multiline_prompt():
|
||||
def multiline_prompt(curr=''):
|
||||
"""Returns the filler text for the prompt in multiline scenarios."""
|
||||
curr = builtins.__xonsh_env__.get('PROMPT')
|
||||
curr = format_prompt(curr)
|
||||
line = curr.rsplit('\n', 1)[1] if '\n' in curr else curr
|
||||
line = RE_HIDDEN.sub('', line) # gets rid of colors
|
||||
# most prompts end in whitespace, head is the part before that.
|
||||
|
|
|
@ -17,8 +17,12 @@ import io as stdlib_io
|
|||
from collections import namedtuple
|
||||
|
||||
from xonsh import openpy
|
||||
from xonsh.tools import (cast_unicode, safe_hasattr, string_types, indent,
|
||||
VER_MAJOR_MINOR, VER_3_4)
|
||||
from xonsh.tools import (cast_unicode, safe_hasattr, string_types, indent,
|
||||
VER_MAJOR_MINOR, VER_3_4, print_color, format_color, HAVE_PYGMENTS)
|
||||
|
||||
if HAVE_PYGMENTS:
|
||||
import pygments
|
||||
from xonsh import pyghooks
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
ISPY3K = True
|
||||
|
@ -297,7 +301,7 @@ def find_source_lines(obj):
|
|||
|
||||
|
||||
if VER_MAJOR_MINOR <= VER_3_4:
|
||||
FrameInfo = namedtuple('FrameInfo', ['frame', 'filename', 'lineno', 'function',
|
||||
FrameInfo = namedtuple('FrameInfo', ['frame', 'filename', 'lineno', 'function',
|
||||
'code_context', 'index'])
|
||||
def getouterframes(frame, context=1):
|
||||
"""Wrapper for getouterframes so that it acts like the Python v3.5 version."""
|
||||
|
@ -420,8 +424,8 @@ class Inspector(object):
|
|||
o = openpy.read_py_file(ofile, skip_encoding_cookie=False)
|
||||
print(o, lineno - 1)
|
||||
|
||||
def _format_fields(self, fields, title_width=0):
|
||||
"""Formats a list of fields for display.
|
||||
def _format_fields_str(self, fields, title_width=0):
|
||||
"""Formats a list of fields for display using color strings.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
@ -434,12 +438,61 @@ class Inspector(object):
|
|||
if title_width == 0:
|
||||
title_width = max(len(title) + 2 for title, _ in fields)
|
||||
for title, content in fields:
|
||||
title_len = len(title)
|
||||
title = '{BOLD_RED}' + title + ':{NO_COLOR}'
|
||||
if len(content.splitlines()) > 1:
|
||||
title = title + ":\n"
|
||||
title += '\n'
|
||||
else:
|
||||
title = (title + ":").ljust(title_width)
|
||||
title += " ".ljust(title_width - title_len)
|
||||
out.append(cast_unicode(title) + cast_unicode(content))
|
||||
return "\n".join(out)
|
||||
return format_color("\n".join(out) + '\n')
|
||||
|
||||
def _format_fields_tokens(self, fields, title_width=0):
|
||||
"""Formats a list of fields for display using color tokens from
|
||||
pygments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fields : list
|
||||
A list of 2-tuples: (field_title, field_content)
|
||||
title_width : int
|
||||
How many characters to pad titles to. Default to longest title.
|
||||
"""
|
||||
out = []
|
||||
if title_width == 0:
|
||||
title_width = max(len(title) + 2 for title, _ in fields)
|
||||
for title, content in fields:
|
||||
title_len = len(title)
|
||||
title = '{BOLD_RED}' + title + ':{NO_COLOR}'
|
||||
if not isinstance(content, str) or len(content.splitlines()) > 1:
|
||||
title += '\n'
|
||||
else:
|
||||
title += " ".ljust(title_width - title_len)
|
||||
out += pyghooks.partial_color_tokenize(title)
|
||||
if isinstance(content, str):
|
||||
out[-1] = (out[-1][0], out[-1][1] + content + '\n')
|
||||
else:
|
||||
out += content
|
||||
out[-1] = (out[-1][0], out[-1][1] + '\n')
|
||||
out[-1] = (out[-1][0], out[-1][1] + '\n')
|
||||
return out
|
||||
|
||||
def _format_fields(self, fields, title_width=0):
|
||||
"""Formats a list of fields for display using color tokens from
|
||||
pygments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fields : list
|
||||
A list of 2-tuples: (field_title, field_content)
|
||||
title_width : int
|
||||
How many characters to pad titles to. Default to longest title.
|
||||
"""
|
||||
if HAVE_PYGMENTS:
|
||||
rtn = self._format_fields_tokens(fields, title_width=title_width)
|
||||
else:
|
||||
rtn = self._format_fields_str(fields, title_width=title_width)
|
||||
return rtn
|
||||
|
||||
# The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
|
||||
pinfo_fields1 = [("Type", "type_name")]
|
||||
|
@ -521,7 +574,7 @@ class Inspector(object):
|
|||
|
||||
# Finally send to printer/pager:
|
||||
if displayfields:
|
||||
print(self._format_fields(displayfields))
|
||||
print_color(self._format_fields(displayfields))
|
||||
|
||||
def info(self, obj, oname='', info=None, detail_level=0):
|
||||
"""Compute a dict with detailed information about an object.
|
||||
|
@ -635,7 +688,11 @@ class Inspector(object):
|
|||
if hasattr(obj, '__class__'):
|
||||
source = getsource(obj.__class__, binary_file)
|
||||
if source is not None:
|
||||
out['source'] = source.rstrip()
|
||||
source = source.rstrip()
|
||||
if HAVE_PYGMENTS:
|
||||
lexer = pyghooks.XonshLexer()
|
||||
source = list(pygments.lex(source, lexer=lexer))
|
||||
out['source'] = source
|
||||
except Exception: # pylint:disable=broad-except
|
||||
pass
|
||||
|
||||
|
|
|
@ -13,8 +13,14 @@ except ImportError:
|
|||
|
||||
from xonsh import __version__
|
||||
from xonsh.shell import Shell
|
||||
from xonsh.pretty import pprint
|
||||
from xonsh.pretty import pprint, pretty
|
||||
from xonsh.jobs import ignore_sigtstp
|
||||
from xonsh.tools import HAVE_PYGMENTS, print_color
|
||||
|
||||
if HAVE_PYGMENTS:
|
||||
import pygments
|
||||
from xonsh import pyghooks
|
||||
|
||||
|
||||
def path_argument(s):
|
||||
"""Return a path only if the path is actually legal
|
||||
|
@ -123,7 +129,13 @@ def undo_args(args):
|
|||
def _pprint_displayhook(value):
|
||||
if value is not None:
|
||||
builtins._ = None # Set '_' to None to avoid recursion
|
||||
pprint(value)
|
||||
if HAVE_PYGMENTS:
|
||||
s = pretty(value) # color case
|
||||
lexer = pyghooks.XonshLexer()
|
||||
tokens = list(pygments.lex(s, lexer=lexer))
|
||||
print_color(tokens)
|
||||
else:
|
||||
pprint(value) # black & white case
|
||||
builtins._ = value
|
||||
|
||||
|
||||
|
|
|
@ -14,13 +14,14 @@ from pygments.token import (Keyword, Name, Comment, String, Error, Number,
|
|||
Operator, Generic, Whitespace, Token)
|
||||
|
||||
from xonsh.base_shell import BaseShell
|
||||
from xonsh.tools import (format_prompt_for_prompt_toolkit, _make_style,
|
||||
print_exception, format_color)
|
||||
from xonsh.tools import print_exception, format_color
|
||||
from xonsh.environ import partial_format_prompt
|
||||
from xonsh.pyghooks import XonshLexer, XonshStyle, partial_color_tokenize, \
|
||||
xonsh_style_proxy
|
||||
from xonsh.ptk.completer import PromptToolkitCompleter
|
||||
from xonsh.ptk.history import PromptToolkitHistory
|
||||
from xonsh.ptk.key_bindings import load_xonsh_bindings
|
||||
from xonsh.ptk.shortcuts import Prompter, print_tokens
|
||||
from xonsh.pyghooks import XonshLexer
|
||||
|
||||
|
||||
class PromptToolkitShell(BaseShell):
|
||||
|
@ -39,13 +40,12 @@ class PromptToolkitShell(BaseShell):
|
|||
enable_open_in_editor=True)
|
||||
load_xonsh_bindings(self.key_bindings_manager)
|
||||
|
||||
def singleline(self, store_in_history=True, auto_suggest=None,
|
||||
def singleline(self, store_in_history=True, auto_suggest=None,
|
||||
enable_history_search=True, multiline=True, **kwargs):
|
||||
"""Reads a single line of input from the shell. The store_in_history
|
||||
kwarg flags whether the input should be stored in PTK's in-memory
|
||||
history.
|
||||
"""
|
||||
token_func, style_cls = self._get_prompt_tokens_and_style()
|
||||
env = builtins.__xonsh_env__
|
||||
mouse_support = env.get('MOUSE_SUPPORT')
|
||||
if store_in_history:
|
||||
|
@ -56,16 +56,17 @@ class PromptToolkitShell(BaseShell):
|
|||
auto_suggest = auto_suggest if env.get('AUTO_SUGGEST') else None
|
||||
completions_display = env.get('COMPLETIONS_DISPLAY')
|
||||
multicolumn = (completions_display == 'multi')
|
||||
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
||||
completer = None if completions_display == 'none' else self.pt_completer
|
||||
with self.prompter:
|
||||
line = self.prompter.prompt(
|
||||
mouse_support=mouse_support,
|
||||
auto_suggest=auto_suggest,
|
||||
get_prompt_tokens=token_func,
|
||||
style=style_cls,
|
||||
get_prompt_tokens=self.prompt_tokens,
|
||||
style=PygmentsStyle(xonsh_style_proxy(self.styler)),
|
||||
completer=completer,
|
||||
lexer=PygmentsLexer(XonshLexer),
|
||||
multiline=multiline,
|
||||
multiline=multiline,
|
||||
history=history,
|
||||
enable_history_search=enable_history_search,
|
||||
reserve_space_for_menu=0,
|
||||
|
@ -115,80 +116,31 @@ class PromptToolkitShell(BaseShell):
|
|||
else:
|
||||
break
|
||||
|
||||
def _get_prompt_tokens_and_style(self):
|
||||
"""Returns function to pass as prompt to prompt_toolkit."""
|
||||
token_names, cstyles, strings = format_prompt_for_prompt_toolkit(self.prompt)
|
||||
tokens = [getattr(Token, n) for n in token_names]
|
||||
def get_tokens(cli):
|
||||
return list(zip(tokens, strings))
|
||||
custom_style = _xonsh_style(tokens, cstyles)
|
||||
return get_tokens, custom_style
|
||||
def prompt_tokens(self, cli):
|
||||
"""Returns a list of (token, str) tuples for the current prompt."""
|
||||
p = builtins.__xonsh_env__.get('PROMPT')
|
||||
try:
|
||||
p = partial_format_prompt(p)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
print_exception()
|
||||
toks = partial_color_tokenize(p)
|
||||
self.settitle()
|
||||
return toks
|
||||
|
||||
def print_color(self, string,end='\n', **kwargs):
|
||||
def format_color(self, string, **kwargs):
|
||||
"""Formats a color string using Pygments. This, therefore, returns
|
||||
a list of (Token, str) tuples.
|
||||
"""
|
||||
return partial_color_tokenize(string)
|
||||
|
||||
def print_color(self, string, end='\n', **kwargs):
|
||||
"""Prints a color string using prompt-toolkit color management."""
|
||||
s = format_color(string + end, remove_escapes=False)
|
||||
token_names, cstyles, strings = format_prompt_for_prompt_toolkit(s)
|
||||
toks = [getattr(Token, n) for n in token_names]
|
||||
custom_style = PygmentsStyle(_xonsh_style(toks, cstyles))
|
||||
tokens = list(zip(toks, strings))
|
||||
print_tokens(tokens, style=custom_style)
|
||||
|
||||
|
||||
def _xonsh_style(tokens=tuple(), cstyles=tuple()):
|
||||
class XonshStyle(Style):
|
||||
styles = {
|
||||
Whitespace: "GRAY",
|
||||
Comment: "UNDERLINE INTENSE GRAY",
|
||||
Comment.Preproc: "UNDERLINE INTENSE GRAY",
|
||||
Keyword: "BOLD GREEN",
|
||||
Keyword.Pseudo: "GREEN",
|
||||
Keyword.Type: "MAGENTA",
|
||||
Operator: "GRAY",
|
||||
Operator.Word: "BOLD",
|
||||
Name.Builtin: "INTENSE GREEN",
|
||||
Name.Function: "BLUE",
|
||||
Name.Class: "BOLD BLUE",
|
||||
Name.Namespace: "BOLD BLUE",
|
||||
Name.Exception: "BOLD INTENSE RED",
|
||||
Name.Variable: "CYAN",
|
||||
Name.Constant: "RED",
|
||||
Name.Label: "YELLOW",
|
||||
Name.Entity: "BOLD WHITE",
|
||||
Name.Attribute: "CYAN",
|
||||
Name.Tag: "BOLD GREEN",
|
||||
Name.Decorator: "CYAN",
|
||||
String: "MAGENTA",
|
||||
String.Doc: "UNDERLINE MAGENTA",
|
||||
String.Interpol: "BOLD MAGENTA",
|
||||
String.Escape: "BOLD RED",
|
||||
String.Regex: "MAGENTA",
|
||||
String.Symbol: "BOLD GREEN",
|
||||
String.Other: "GREEN",
|
||||
Number: "RED",
|
||||
Generic.Heading: "BOLD BLUE",
|
||||
Generic.Subheading: "BOLD MAGENTA",
|
||||
Generic.Deleted: "RED",
|
||||
Generic.Inserted: "GREEN",
|
||||
Generic.Error: "BOLD RED",
|
||||
Generic.Emph: "UNDERLINE",
|
||||
Generic.Prompt: "BOLD BLUE",
|
||||
Generic.Output: "GRAY",
|
||||
Generic.Traceback: "RED",
|
||||
Error: "RED",
|
||||
}
|
||||
styles = {k: _make_style(v) for k, v in styles.items()}
|
||||
styles.update({
|
||||
Token.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000',
|
||||
Token.Menu.Completions.Completion: 'bg:#008888 #ffffff',
|
||||
Token.Menu.Completions.ProgressButton: 'bg:#003333',
|
||||
Token.Menu.Completions.ProgressBar: 'bg:#00aaaa',
|
||||
Token.AutoSuggestion: '#666666',
|
||||
Token.Aborted: '#888888',
|
||||
})
|
||||
# update with the prompt styles
|
||||
styles.update(dict(zip(tokens, cstyles)))
|
||||
# Update with with any user styles
|
||||
userstyle = builtins.__xonsh_env__.get('PROMPT_TOOLKIT_STYLES')
|
||||
if userstyle is not None:
|
||||
styles.update(userstyle)
|
||||
return XonshStyle
|
||||
if isinstance(string, str):
|
||||
tokens = partial_color_tokenize(string + end)
|
||||
else:
|
||||
# assume this is a list of (Token, str) tuples and just print
|
||||
tokens = string
|
||||
env = builtins.__xonsh_env__
|
||||
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
||||
proxy_style = PygmentsStyle(xonsh_style_proxy(self.styler))
|
||||
print_tokens(tokens, style=proxy_style)
|
||||
|
|
2464
xonsh/pyghooks.py
2464
xonsh/pyghooks.py
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,14 @@ from collections import deque
|
|||
|
||||
from xonsh import lazyjson
|
||||
from xonsh.base_shell import BaseShell
|
||||
from xonsh.tools import ON_WINDOWS, print_color
|
||||
from xonsh.ansi_colors import partial_color_format
|
||||
from xonsh.environ import partial_format_prompt, multiline_prompt
|
||||
from xonsh.tools import ON_WINDOWS, print_exception, HAVE_PYGMENTS
|
||||
|
||||
if HAVE_PYGMENTS:
|
||||
from xonsh import pyghooks
|
||||
import pygments
|
||||
from pygments.formatters.terminal256 import Terminal256Formatter
|
||||
|
||||
RL_COMPLETION_SUPPRESS_APPEND = RL_LIB = None
|
||||
RL_CAN_RESIZE = False
|
||||
|
@ -96,6 +103,7 @@ class ReadlineShell(BaseShell, Cmd):
|
|||
**kwargs)
|
||||
setup_readline()
|
||||
self._current_indent = ''
|
||||
self._current_prompt = ''
|
||||
self.cmdqueue = deque()
|
||||
|
||||
def __del__(self):
|
||||
|
@ -103,7 +111,7 @@ class ReadlineShell(BaseShell, Cmd):
|
|||
|
||||
def singleline(self, store_in_history=True, **kwargs):
|
||||
"""Reads a single line of input. The store_in_history kwarg
|
||||
flags whether the input should be stored in readline's in-memory
|
||||
flags whether the input should be stored in readline's in-memory
|
||||
history.
|
||||
"""
|
||||
if not store_in_history: # store current position to remove it later
|
||||
|
@ -225,7 +233,7 @@ class ReadlineShell(BaseShell, Cmd):
|
|||
if inserter is not None:
|
||||
readline.set_pre_input_hook(None)
|
||||
else:
|
||||
print_color(self.prompt, file=self.stdout)
|
||||
self.print_color(self.prompt, file=self.stdout)
|
||||
if line is not None:
|
||||
os.write(self.stdin.fileno(), line.encode())
|
||||
if not exec_now:
|
||||
|
@ -268,7 +276,45 @@ class ReadlineShell(BaseShell, Cmd):
|
|||
# This is needed to support some system where line-wrapping doesn't
|
||||
# work. This is a bug in upstream Python, or possibly readline.
|
||||
RL_LIB.rl_reset_screen_size()
|
||||
return super().prompt
|
||||
#return super().prompt
|
||||
if self.need_more_lines:
|
||||
if self.mlprompt is None:
|
||||
try:
|
||||
self.mlprompt = multiline_prompt(curr=self._current_prompt)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
print_exception()
|
||||
self.mlprompt = '<multiline prompt error> '
|
||||
return self.mlprompt
|
||||
env = builtins.__xonsh_env__ # pylint: disable=no-member
|
||||
p = env.get('PROMPT')
|
||||
try:
|
||||
p = partial_format_prompt(p)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
print_exception()
|
||||
p = partial_color_format(p, style=env.get('XONSH_COLOR_STYLE'),
|
||||
hide=True)
|
||||
self._current_prompt = p
|
||||
self.settitle()
|
||||
return p
|
||||
|
||||
def format_color(self, string, hide=False, **kwargs):
|
||||
"""Readline implementation of color formatting. This usesg ANSI color
|
||||
codes.
|
||||
"""
|
||||
return partial_color_format(string, hide=hide,
|
||||
style=builtins.__xonsh_env__.get('XONSH_COLOR_STYLE'))
|
||||
|
||||
def print_color(self, string, hide=False, **kwargs):
|
||||
if isinstance(string, str):
|
||||
s = self.format_color(string, hide=hide)
|
||||
else:
|
||||
# assume this is a list of (Token, str) tuples and format it
|
||||
env = builtins.__xonsh_env__
|
||||
self.styler.style_name = env.get('XONSH_COLOR_STYLE')
|
||||
style_proxy = pyghooks.xonsh_style_proxy(self.styler)
|
||||
formatter = Terminal256Formatter(style=style_proxy)
|
||||
s = pygments.format(string, formatter).rstrip()
|
||||
print(s, **kwargs)
|
||||
|
||||
|
||||
class ReadlineHistoryAdder(Thread):
|
||||
|
@ -307,4 +353,3 @@ class ReadlineHistoryAdder(Thread):
|
|||
lj.close()
|
||||
except (IOError, OSError):
|
||||
continue
|
||||
|
||||
|
|
210
xonsh/tools.py
210
xonsh/tools.py
|
@ -30,6 +30,19 @@ from contextlib import contextmanager
|
|||
from collections import OrderedDict, Sequence, Set
|
||||
from warnings import warn
|
||||
|
||||
#
|
||||
# Check pygments
|
||||
#
|
||||
|
||||
def pygments_version():
|
||||
"""Returns the Pygments version or False."""
|
||||
try:
|
||||
import pygments
|
||||
v = pygments.__version__
|
||||
except ImportError:
|
||||
v = False
|
||||
return v
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
string_types = (str, bytes)
|
||||
unicode_type = str
|
||||
|
@ -37,6 +50,7 @@ else:
|
|||
string_types = (str, unicode)
|
||||
unicode_type = unicode
|
||||
|
||||
|
||||
DEFAULT_ENCODING = sys.getdefaultencoding()
|
||||
|
||||
ON_WINDOWS = (platform.system() == 'Windows')
|
||||
|
@ -45,6 +59,7 @@ ON_LINUX = (platform.system() == 'Linux')
|
|||
ON_ARCH = (platform.linux_distribution()[0] == 'arch')
|
||||
ON_POSIX = (os.name == 'posix')
|
||||
IS_ROOT = ctypes.windll.shell32.IsUserAnAdmin() != 0 if ON_WINDOWS else os.getuid() == 0
|
||||
HAVE_PYGMENTS = bool(pygments_version())
|
||||
|
||||
VER_3_4 = (3, 4)
|
||||
VER_3_5 = (3, 5)
|
||||
|
@ -223,74 +238,6 @@ def get_sep():
|
|||
os.sep)
|
||||
|
||||
|
||||
TERM_COLORS = {
|
||||
# Reset
|
||||
'NO_COLOR': '\001\033[0m\002', # Text Reset
|
||||
# Regular Colors
|
||||
'BLACK': '\001\033[0;30m\002', # BLACK
|
||||
'RED': '\001\033[0;31m\002', # RED
|
||||
'GREEN': '\001\033[0;32m\002', # GREEN
|
||||
'YELLOW': '\001\033[0;33m\002', # YELLOW
|
||||
'BLUE': '\001\033[0;34m\002', # BLUE
|
||||
'PURPLE': '\001\033[0;35m\002', # PURPLE
|
||||
'CYAN': '\001\033[0;36m\002', # CYAN
|
||||
'WHITE': '\001\033[0;37m\002', # WHITE
|
||||
# Bold
|
||||
'BOLD_BLACK': '\001\033[1;30m\002', # BLACK
|
||||
'BOLD_RED': '\001\033[1;31m\002', # RED
|
||||
'BOLD_GREEN': '\001\033[1;32m\002', # GREEN
|
||||
'BOLD_YELLOW': '\001\033[1;33m\002', # YELLOW
|
||||
'BOLD_BLUE': '\001\033[1;34m\002', # BLUE
|
||||
'BOLD_PURPLE': '\001\033[1;35m\002', # PURPLE
|
||||
'BOLD_CYAN': '\001\033[1;36m\002', # CYAN
|
||||
'BOLD_WHITE': '\001\033[1;37m\002', # WHITE
|
||||
# Underline
|
||||
'UNDERLINE_BLACK': '\001\033[4;30m\002', # BLACK
|
||||
'UNDERLINE_RED': '\001\033[4;31m\002', # RED
|
||||
'UNDERLINE_GREEN': '\001\033[4;32m\002', # GREEN
|
||||
'UNDERLINE_YELLOW': '\001\033[4;33m\002', # YELLOW
|
||||
'UNDERLINE_BLUE': '\001\033[4;34m\002', # BLUE
|
||||
'UNDERLINE_PURPLE': '\001\033[4;35m\002', # PURPLE
|
||||
'UNDERLINE_CYAN': '\001\033[4;36m\002', # CYAN
|
||||
'UNDERLINE_WHITE': '\001\033[4;37m\002', # WHITE
|
||||
# Background
|
||||
'BACKGROUND_BLACK': '\001\033[40m\002', # BLACK
|
||||
'BACKGROUND_RED': '\001\033[41m\002', # RED
|
||||
'BACKGROUND_GREEN': '\001\033[42m\002', # GREEN
|
||||
'BACKGROUND_YELLOW': '\001\033[43m\002', # YELLOW
|
||||
'BACKGROUND_BLUE': '\001\033[44m\002', # BLUE
|
||||
'BACKGROUND_PURPLE': '\001\033[45m\002', # PURPLE
|
||||
'BACKGROUND_CYAN': '\001\033[46m\002', # CYAN
|
||||
'BACKGROUND_WHITE': '\001\033[47m\002', # WHITE
|
||||
# High Intensity
|
||||
'INTENSE_BLACK': '\001\033[0;90m\002', # BLACK
|
||||
'INTENSE_RED': '\001\033[0;91m\002', # RED
|
||||
'INTENSE_GREEN': '\001\033[0;92m\002', # GREEN
|
||||
'INTENSE_YELLOW': '\001\033[0;93m\002', # YELLOW
|
||||
'INTENSE_BLUE': '\001\033[0;94m\002', # BLUE
|
||||
'INTENSE_PURPLE': '\001\033[0;95m\002', # PURPLE
|
||||
'INTENSE_CYAN': '\001\033[0;96m\002', # CYAN
|
||||
'INTENSE_WHITE': '\001\033[0;97m\002', # WHITE
|
||||
# Bold High Intensity
|
||||
'BOLD_INTENSE_BLACK': '\001\033[1;90m\002', # BLACK
|
||||
'BOLD_INTENSE_RED': '\001\033[1;91m\002', # RED
|
||||
'BOLD_INTENSE_GREEN': '\001\033[1;92m\002', # GREEN
|
||||
'BOLD_INTENSE_YELLOW': '\001\033[1;93m\002', # YELLOW
|
||||
'BOLD_INTENSE_BLUE': '\001\033[1;94m\002', # BLUE
|
||||
'BOLD_INTENSE_PURPLE': '\001\033[1;95m\002', # PURPLE
|
||||
'BOLD_INTENSE_CYAN': '\001\033[1;96m\002', # CYAN
|
||||
'BOLD_INTENSE_WHITE': '\001\033[1;97m\002', # WHITE
|
||||
# High Intensity backgrounds
|
||||
'BACKGROUND_INTENSE_BLACK': '\001\033[0;100m\002', # BLACK
|
||||
'BACKGROUND_INTENSE_RED': '\001\033[0;101m\002', # RED
|
||||
'BACKGROUND_INTENSE_GREEN': '\001\033[0;102m\002', # GREEN
|
||||
'BACKGROUND_INTENSE_YELLOW': '\001\033[0;103m\002', # YELLOW
|
||||
'BACKGROUND_INTENSE_BLUE': '\001\033[0;104m\002', # BLUE
|
||||
'BACKGROUND_INTENSE_PURPLE': '\001\033[0;105m\002', # PURPLE
|
||||
'BACKGROUND_INTENSE_CYAN': '\001\033[0;106m\002', # CYAN
|
||||
'BACKGROUND_INTENSE_WHITE': '\001\033[0;107m\002', # WHITE
|
||||
}
|
||||
|
||||
|
||||
def fallback(cond, backup):
|
||||
"""Decorator for returning the object if cond is true and a backup if cond is false.
|
||||
|
@ -737,128 +684,19 @@ def history_tuple_to_str(x):
|
|||
"""Converts a valid history tuple to a canonical string."""
|
||||
return '{0} {1}'.format(*x)
|
||||
|
||||
#
|
||||
# Check pygments
|
||||
#
|
||||
|
||||
def pygments_version():
|
||||
"""Returns the Pygments version or False."""
|
||||
try:
|
||||
import pygments
|
||||
v = pygments.__version__
|
||||
except ImportError:
|
||||
v = False
|
||||
return v
|
||||
|
||||
#
|
||||
# prompt toolkit tools
|
||||
#
|
||||
|
||||
|
||||
COLOR_CODE_SPLIT_RE = re.compile(r'(\001\033\[[\d;m]+\002)')
|
||||
TERM_COLORS_REVERSED = {v: k for k, v in TERM_COLORS.items()}
|
||||
COLOR_NAME_REGEX = re.compile(r'(?:(\w+)_)?(\w+)')
|
||||
|
||||
_PT_COLORS = {
|
||||
'BLACK': '#000000',
|
||||
'RED': '#800000',
|
||||
'GREEN': '#008000',
|
||||
'YELLOW': '#808000',
|
||||
'BLUE': '#000080',
|
||||
'PURPLE': '#800080',
|
||||
'CYAN': '#008080',
|
||||
'WHITE': '#ffffff',
|
||||
'GRAY': '#008080',
|
||||
'INTENSE_RED': '#ff1010',
|
||||
'INTENSE_GREEN': '#00ff18',
|
||||
'INTENSE_YELLOW': '#ffff00',
|
||||
'INTENSE_BLUE': '#0000d2',
|
||||
'INTENSE_PURPLE': '#ff00ff',
|
||||
'INTENSE_CYAN': '#00ffff',
|
||||
'INTENSE_GRAY': '#c0c0c0',
|
||||
None: '',
|
||||
}
|
||||
|
||||
_PT_STYLE = {
|
||||
'BOLD': 'bold',
|
||||
'UNDERLINE': 'underline',
|
||||
}
|
||||
|
||||
_LAST_BG_COLOR = None
|
||||
|
||||
|
||||
def _make_style(color_name):
|
||||
""" Convert color names to pygments styles codes """
|
||||
global _LAST_BG_COLOR
|
||||
|
||||
colors = _PT_COLORS.copy()
|
||||
# Extend with custom colors
|
||||
colors.update(builtins.__xonsh_env__.get('PROMPT_TOOLKIT_COLORS'))
|
||||
|
||||
if color_name == 'NO_COLOR':
|
||||
_LAST_BG_COLOR = None
|
||||
return 'noinherit'
|
||||
|
||||
qualifiers, name = COLOR_NAME_REGEX.match(color_name).groups()
|
||||
|
||||
if name not in colors:
|
||||
qualifiers = name
|
||||
name = None
|
||||
|
||||
qualifiers = qualifiers.split('_') if qualifiers else []
|
||||
is_bg_color = False
|
||||
style = []
|
||||
|
||||
for qualifier in qualifiers:
|
||||
if qualifier == 'INTENSE' and name is not None:
|
||||
name = 'INTENSE_' + name
|
||||
elif qualifier == 'BACKGROUND':
|
||||
is_bg_color = True
|
||||
elif qualifier in _PT_STYLE:
|
||||
style.append(_PT_STYLE[qualifier])
|
||||
|
||||
color = colors[name]
|
||||
if is_bg_color:
|
||||
_LAST_BG_COLOR = color = 'bg:' + color
|
||||
elif _LAST_BG_COLOR:
|
||||
style.append(_LAST_BG_COLOR)
|
||||
|
||||
style.append(color)
|
||||
|
||||
return ' '.join(style)
|
||||
|
||||
|
||||
def format_prompt_for_prompt_toolkit(prompt):
|
||||
"""Converts a prompt with color codes to a pygments style and tokens
|
||||
def format_color(string, **kwargs):
|
||||
"""Formats strings that may contain colors. This simply dispatches to the
|
||||
shell instances method of the same name. The results of this function should
|
||||
be directly usable by print_color().
|
||||
"""
|
||||
parts = COLOR_CODE_SPLIT_RE.split(prompt)
|
||||
# ensure that parts is [colorcode, string, colorcode, string,...]
|
||||
if len(parts[0]) == 0:
|
||||
parts = parts[1:]
|
||||
else:
|
||||
parts.insert(0, '')
|
||||
|
||||
if len(parts) % 2 != 0:
|
||||
parts.append('')
|
||||
|
||||
strings = parts[1::2]
|
||||
token_names = [TERM_COLORS_REVERSED.get(c, 'NO_COLOR') for c in parts[::2]]
|
||||
cstyles = [_make_style(c) for c in token_names]
|
||||
return token_names, cstyles, strings
|
||||
|
||||
|
||||
def format_color(string, remove_escapes=True):
|
||||
"""Formats strings that contain xonsh.tools.TERM_COLORS values."""
|
||||
s = string.format(**TERM_COLORS)
|
||||
if remove_escapes:
|
||||
s = s.replace('\001', '').replace('\002', '')
|
||||
return s
|
||||
return builtins.__xonsh_shell__.shell.format_color(string, **kwargs)
|
||||
|
||||
|
||||
def print_color(string, **kwargs):
|
||||
"""Print strings that contain xonsh.tools.TERM_COLORS values. By default
|
||||
`sys.stdout` is used as the output stream but an alternate can be specified
|
||||
by the `file` keyword argument.
|
||||
"""Prints a string that may contain colors. This dispatched to the shell
|
||||
method of the same name. Colors will be formatted if they have not already
|
||||
been.
|
||||
"""
|
||||
builtins.__xonsh_shell__.shell.print_color(string, **kwargs)
|
||||
|
||||
|
@ -1073,7 +911,7 @@ def expandvars(path):
|
|||
|
||||
#
|
||||
# File handling tools
|
||||
#
|
||||
#
|
||||
|
||||
def backup_file(fname):
|
||||
"""Moves an existing file to a new name that has the current time right
|
||||
|
|
|
@ -7,16 +7,14 @@ import linecache
|
|||
from functools import lru_cache
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from xonsh.tools import (DefaultNotGiven, print_color, pygments_version, normabspath,
|
||||
to_bool)
|
||||
from xonsh.tools import (DefaultNotGiven, print_color, pygments_version,
|
||||
format_color, normabspath, to_bool, HAVE_PYGMENTS)
|
||||
from xonsh import inspectors
|
||||
from xonsh.environ import _replace_home as replace_home
|
||||
if pygments_version():
|
||||
if HAVE_PYGMENTS:
|
||||
from xonsh import pyghooks
|
||||
import pygments
|
||||
import pygments.formatters.terminal
|
||||
else:
|
||||
pyghooks = None
|
||||
|
||||
class TracerType(object):
|
||||
"""Represents a xonsh tracer object, which keeps track of all tracing
|
||||
|
@ -28,7 +26,7 @@ class TracerType(object):
|
|||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._inst is None:
|
||||
cls._inst = super(TracerType, cls).__new__(cls, *args,
|
||||
cls._inst = super(TracerType, cls).__new__(cls, *args,
|
||||
**kwargs)
|
||||
return cls._inst
|
||||
|
||||
|
@ -79,7 +77,7 @@ class TracerType(object):
|
|||
if curr != self._last:
|
||||
line = linecache.getline(fname, lineno).rstrip()
|
||||
s = format_line(fname, lineno, line, color=self.usecolor,
|
||||
lexer=self.lexer, formatter=self.formatter).rstrip()
|
||||
lexer=self.lexer, formatter=self.formatter)
|
||||
print_color(s)
|
||||
self._last = curr
|
||||
return self.trace
|
||||
|
@ -90,30 +88,22 @@ tracer = TracerType()
|
|||
COLORLESS_LINE = '{fname}:{lineno}:{line}'
|
||||
COLOR_LINE = ('{{PURPLE}}{fname}{{BLUE}}:'
|
||||
'{{GREEN}}{lineno}{{BLUE}}:'
|
||||
'{{NO_COLOR}}{line}')
|
||||
'{{NO_COLOR}}')
|
||||
|
||||
RAW_COLOR_CODE_RE = re.compile(r'(\033\[[\d;]+m)')
|
||||
def _escape_code_adder(m):
|
||||
return '\001' + m.group(1) + '\002'
|
||||
|
||||
NO_SEMI_COLOR_CODE_RE = re.compile(r'(\033\[\d+m)')
|
||||
def _0semi_adder(m):
|
||||
return m.group(1).replace('[', '[0;')
|
||||
|
||||
def format_line(fname, lineno, line, color=True, lexer=None, formatter=None):
|
||||
"""Formats a trace line suitable for printing."""
|
||||
fname = min(fname, replace_home(fname), os.path.relpath(fname), key=len)
|
||||
if not color:
|
||||
return COLORLESS_LINE.format(fname=fname, lineno=lineno, line=line)
|
||||
if pyghooks is not None:
|
||||
lexer = lexer or pyghooks.XonshLexer()
|
||||
formatter = formatter or pygments.formatters.terminal.TerminalFormatter()
|
||||
line = pygments.highlight(line, lexer, formatter)
|
||||
line = line.replace('{', '{{').replace('}', '}}')
|
||||
line = line.replace('\033[39;49;00m', '\033[0m')
|
||||
line = NO_SEMI_COLOR_CODE_RE.sub(_0semi_adder, line)
|
||||
line = RAW_COLOR_CODE_RE.sub(_escape_code_adder, line)
|
||||
return COLOR_LINE.format(fname=fname, lineno=lineno, line=line)
|
||||
cline = COLOR_LINE.format(fname=fname, lineno=lineno)
|
||||
if not HAVE_PYGMENTS:
|
||||
return cline + line
|
||||
# OK, so we have pygments
|
||||
tokens = pyghooks.partial_color_tokenize(cline)
|
||||
lexer = lexer or pyghooks.XonshLexer()
|
||||
tokens += pygments.lex(line, lexer=lexer)
|
||||
return tokens
|
||||
|
||||
|
||||
#
|
||||
|
@ -128,7 +118,7 @@ def _find_caller(args):
|
|||
return fname
|
||||
elif lineno == 1 and re_line.search(linecache.getline(fname, lineno)) is not None:
|
||||
# There is a bug in CPython such that inspectors.getouterframes(curr, context=1)
|
||||
# will actually return the 2nd line in the code_context field, even though
|
||||
# will actually return the 2nd line in the code_context field, even though
|
||||
# line number is itself correct. We manually fix that in this branch.
|
||||
return fname
|
||||
else:
|
||||
|
@ -171,16 +161,16 @@ def _create_parser():
|
|||
subp = p.add_subparsers(title='action', dest='action')
|
||||
onp = subp.add_parser('on', aliases=['start', 'add'],
|
||||
help='begins tracing selected files.')
|
||||
onp.add_argument('files', nargs='*', default=['__file__'],
|
||||
onp.add_argument('files', nargs='*', default=['__file__'],
|
||||
help=('file paths to watch, use "__file__" (default) to select '
|
||||
'the current file.'))
|
||||
off = subp.add_parser('off', aliases=['stop', 'del', 'rm'],
|
||||
help='removes selected files fom tracing.')
|
||||
off.add_argument('files', nargs='*', default=['__file__'],
|
||||
off.add_argument('files', nargs='*', default=['__file__'],
|
||||
help=('file paths to stop watching, use "__file__" (default) to '
|
||||
'select the current file.'))
|
||||
col = subp.add_parser('color', help='output color management for tracer.')
|
||||
col.add_argument('toggle', type=to_bool,
|
||||
col.add_argument('toggle', type=to_bool,
|
||||
help='true/false, y/n, etc. to toggle color usage.')
|
||||
return p
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue