mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
nice
This commit is contained in:
parent
5aa8b09b63
commit
a62fe6a0cb
3 changed files with 99 additions and 26 deletions
|
@ -11,7 +11,7 @@ from collections.abc import Mapping, Sequence
|
|||
from xonsh.tools import to_bool, to_bool_or_break, backup_file, print_color
|
||||
|
||||
#
|
||||
# Nodes themselves
|
||||
# Nodes themselves
|
||||
#
|
||||
class Node(object):
|
||||
"""Base type of all nodes."""
|
||||
|
@ -79,7 +79,7 @@ class Input(Node):
|
|||
|
||||
attrs = ('prompt', 'converter', 'show_conversion', 'confirm', 'path')
|
||||
|
||||
def __init__(self, prompt='>>> ', converter=None, show_conversion=False,
|
||||
def __init__(self, prompt='>>> ', converter=None, show_conversion=False,
|
||||
confirm=False, path=None):
|
||||
"""
|
||||
Parameters
|
||||
|
@ -90,11 +90,11 @@ class Input(Node):
|
|||
Converts the string the user typed into another object
|
||||
prior to storage.
|
||||
show_conversion : bool, optional
|
||||
Flag for whether or not to show the results of the conversion
|
||||
Flag for whether or not to show the results of the conversion
|
||||
function if the conversion function was meaningfully executed.
|
||||
Default False.
|
||||
confirm : bool, optional
|
||||
Whether the input should be confirmed until true or broken,
|
||||
Whether the input should be confirmed until true or broken,
|
||||
default False.
|
||||
path : str or sequence of str, optional
|
||||
A path within the storage object.
|
||||
|
@ -120,9 +120,9 @@ class While(Node):
|
|||
Parameters
|
||||
----------
|
||||
cond : callable
|
||||
Function that determines if the next loop iteration should
|
||||
be executed. The condition function has the form
|
||||
cond(visitor=None, node=None) and should return an object that
|
||||
Function that determines if the next loop iteration should
|
||||
be executed. The condition function has the form
|
||||
cond(visitor=None, node=None) and should return an object that
|
||||
is convertable to a bool.
|
||||
body : sequence of nodes
|
||||
A list of node to execute on each iteration.
|
||||
|
@ -146,7 +146,7 @@ class While(Node):
|
|||
|
||||
class YesNo(Question):
|
||||
"""Represents a simple yes/no question."""
|
||||
|
||||
|
||||
def __init__(self, question, yes, no, path=None):
|
||||
"""
|
||||
Parameters
|
||||
|
@ -176,7 +176,7 @@ class TrueFalseBreak(Input):
|
|||
"""Input node the returns a True, False, or 'break' value."""
|
||||
|
||||
def __init__(self, prompt='yes, no, or break [default: no]? ', path=None):
|
||||
super().__init__(prompt=prompt, converter=to_bool_or_break,
|
||||
super().__init__(prompt=prompt, converter=to_bool_or_break,
|
||||
show_conversion=False, confirm=False, path=path)
|
||||
|
||||
|
||||
|
@ -185,10 +185,10 @@ class StoreNonEmpty(Input):
|
|||
This works by wrapping the converter function.
|
||||
"""
|
||||
|
||||
def __init__(self, prompt='>>> ', converter=None, show_conversion=False,
|
||||
def __init__(self, prompt='>>> ', converter=None, show_conversion=False,
|
||||
confirm=False, path=None):
|
||||
def nonempty_converter(x):
|
||||
"""Converts non-empty values and converts empty inputs to
|
||||
"""Converts non-empty values and converts empty inputs to
|
||||
Unstorable.
|
||||
"""
|
||||
if len(x) == 0:
|
||||
|
@ -217,12 +217,12 @@ class StateFile(Input):
|
|||
default_file : str, optional
|
||||
The default filename to save the file as.
|
||||
check : bool, optional
|
||||
Whether to print the current state and ask if it should be
|
||||
saved/loaded prior to asking for the file name and saving the
|
||||
Whether to print the current state and ask if it should be
|
||||
saved/loaded prior to asking for the file name and saving the
|
||||
file, default=True.
|
||||
"""
|
||||
self._df = None
|
||||
super().__init__(prompt='filename: ', converter=None,
|
||||
super().__init__(prompt='filename: ', converter=None,
|
||||
confirm=False, path=None)
|
||||
self.default_file = default_file
|
||||
self.check = check
|
||||
|
@ -268,7 +268,7 @@ def create_truefalse_cond(prompt='yes or no [default: no]? ', path=None):
|
|||
|
||||
#
|
||||
# Tools for trees of nodes.
|
||||
#
|
||||
#
|
||||
|
||||
_lowername = lambda cls: cls.__name__.lower()
|
||||
|
||||
|
@ -293,11 +293,11 @@ class Visitor(object):
|
|||
break
|
||||
else:
|
||||
msg = 'could not find valid visitor method for {0} on {1}'
|
||||
nodename = node.__class__.__name__
|
||||
nodename = node.__class__.__name__
|
||||
selfname = self.__class__.__name__
|
||||
raise AttributeError(msg.format(nodename, selfname))
|
||||
return rtn
|
||||
|
||||
|
||||
|
||||
class PrettyFormatter(Visitor):
|
||||
"""Formats a tree of nodes into a pretty string"""
|
||||
|
@ -332,7 +332,7 @@ class PrettyFormatter(Visitor):
|
|||
return s + '], path={0!r})'.format(node.path)
|
||||
s += '\n'
|
||||
self.level += 1
|
||||
s += textwrap.indent(',\n'.join(map(self.visit, node.children)),
|
||||
s += textwrap.indent(',\n'.join(map(self.visit, node.children)),
|
||||
self.indent)
|
||||
self.level -= 1
|
||||
if node.path is None:
|
||||
|
@ -355,7 +355,7 @@ class PrettyFormatter(Visitor):
|
|||
s += '\n'
|
||||
t = sorted(node.responses.items())
|
||||
t = ['{0!r}: {1}'.format(k, self.visit(v)) for k, v in t]
|
||||
s += textwrap.indent(',\n'.join(t), 2*self.indent)
|
||||
s += textwrap.indent(',\n'.join(t), 2*self.indent)
|
||||
s += '\n' + self.indent + '}'
|
||||
if node.converter is not None:
|
||||
s += ',\n' + self.indent + 'converter={0!r}'.format(node.converter)
|
||||
|
@ -388,7 +388,7 @@ class PrettyFormatter(Visitor):
|
|||
if len(node.body) > 0:
|
||||
s += '\n'
|
||||
self.level += 1
|
||||
s += textwrap.indent(',\n'.join(map(self.visit, node.body)),
|
||||
s += textwrap.indent(',\n'.join(map(self.visit, node.body)),
|
||||
self.indent)
|
||||
self.level -= 1
|
||||
s += '\n' + self.indent
|
||||
|
@ -441,7 +441,7 @@ class UnstorableType(object):
|
|||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._inst is None:
|
||||
cls._inst = super(UnstorableType, cls).__new__(cls, *args,
|
||||
cls._inst = super(UnstorableType, cls).__new__(cls, *args,
|
||||
**kwargs)
|
||||
return cls._inst
|
||||
|
||||
|
@ -468,6 +468,8 @@ class StateVisitor(Visitor):
|
|||
raise RuntimeError('no node or tree given!')
|
||||
rtn = super().visit(node)
|
||||
path = getattr(node, 'path', None)
|
||||
if callable(path):
|
||||
path = path(visitor=self, node=node, val=rtn)
|
||||
if path is not None and rtn is not Unstorable:
|
||||
self.store(path, rtn, indices=self.indices)
|
||||
return rtn
|
||||
|
@ -488,6 +490,10 @@ class StateVisitor(Visitor):
|
|||
loc.extend(ex)
|
||||
loc = loc[p]
|
||||
p = path[-1]
|
||||
if isinstance(p, int) and abs(p) + (p >= 0) > len(loc):
|
||||
i = abs(p) + (p >= 0) - len(loc)
|
||||
ex = [None]*i
|
||||
loc.extend(ex)
|
||||
loc[p] = val
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ from xonsh.environ import is_template_string
|
|||
from xonsh.shell import (is_readline_available, is_prompt_toolkit_available,
|
||||
prompt_toolkit_version)
|
||||
from xonsh.wizard import (Wizard, Pass, Message, Save, Load, YesNo, Input,
|
||||
PromptVisitor, While, StoreNonEmpty, create_truefalse_cond, YN)
|
||||
PromptVisitor, While, StoreNonEmpty, create_truefalse_cond, YN, Unstorable)
|
||||
from xonsh.xontribs import xontrib_metadata, find_xontrib
|
||||
|
||||
|
||||
HR = "'`-.,_,.-*'`-.,_,.-*'`-.,_,.-*'`-.,_,.-*'`-.,_,.-*'`-.,_,.-*'`-.,_,.-*'"
|
||||
|
@ -82,6 +83,24 @@ will accept the default value for that entry.
|
|||
|
||||
WIZARD_ENV_QUESTION = "Would you like to set env vars now, " + YN
|
||||
|
||||
WIZARD_XONTRIB = """
|
||||
{hr}
|
||||
|
||||
{{BOLD_WHITE}}Xontribs{{NO_COLOR}}
|
||||
{{YELLOW}}--------{{NO_COLOR}}
|
||||
No shell is complete without extentions, and xonsh is no exception. Xonsh
|
||||
extensions are called {{BOLD_GREEN}}xontribs{{NO_COLOR}}, or xonsh contributions.
|
||||
Xontribs are dynamically loadable, either by importing them directly or by
|
||||
using the 'xontrib' command. However, you can also configure xonsh to load
|
||||
xontribs autoumatically on startup prior to loading the run control files.
|
||||
This allows the xontrib to be used immeadiately in your xonshrc files.
|
||||
|
||||
The following describes all xontribs that have been registered with xonsh.
|
||||
These come from users, 3rd party developers, or xonsh itself!
|
||||
""".format(hr=HR)
|
||||
|
||||
WIZARD_XONTRIB_QUESTION = "Would you like to enable xontribs now, " + YN
|
||||
|
||||
|
||||
WIZARD_TAIL = """
|
||||
Thanks for using the xonsh configuration wizard!"""
|
||||
|
@ -171,9 +190,8 @@ def make_envvar(name):
|
|||
return mnode, pnode
|
||||
|
||||
|
||||
def make_env():
|
||||
"""Makes an environment variable wizard."""
|
||||
kids = map(make_envvar, sorted(builtins.__xonsh_env__.docs.keys()))
|
||||
def _make_flat_wiz(kidfunc, *args):
|
||||
kids = map(kidfunc, *args)
|
||||
flatkids = []
|
||||
for k in kids:
|
||||
if k is None:
|
||||
|
@ -183,6 +201,53 @@ def make_env():
|
|||
return wiz
|
||||
|
||||
|
||||
def make_env():
|
||||
"""Makes an environment variable wizard."""
|
||||
w = _make_flat_wiz(make_envvar, sorted(builtins.__xonsh_env__.docs.keys()))
|
||||
return w
|
||||
|
||||
|
||||
XONTRIB_PROMPT = '{BOLD_GREEN}Add this xontrib{NO_COLOR}, ' + YN
|
||||
|
||||
def _xontrib_path(visitor=None, node=None, val=None):
|
||||
# need this to append only based on user-selected size
|
||||
return ('xontribs', len(visitor.state.get('xontribs', ())))
|
||||
|
||||
|
||||
def make_xontrib(xontrib, package):
|
||||
"""Makes a message and StoreNonEmpty node for a xontrib."""
|
||||
name = xontrib.get('name', '<unknown-xontrib-name>')
|
||||
msg = '\n{BOLD_CYAN}' + name + '{NO_COLOR}\n'
|
||||
if 'url' in xontrib:
|
||||
msg += '{RED}url:{NO_COLOR} ' + xontrib['url'] + '\n'
|
||||
if 'package' in xontrib:
|
||||
msg += '{RED}package:{NO_COLOR} ' + xontrib['package'] + '\n'
|
||||
if 'url' in package:
|
||||
if 'url' in xontrib and package['url'] != xontrib['url']:
|
||||
msg += '{RED}package-url:{NO_COLOR} ' + package['url'] + '\n'
|
||||
if 'license' in package:
|
||||
msg += '{RED}license:{NO_COLOR} ' + package['license'] + '\n'
|
||||
desc = xontrib.get('description', '')
|
||||
if not isinstance(desc, str):
|
||||
desc = ''.join(desc)
|
||||
msg += _wrap_paragraphs(desc, width=69)
|
||||
if msg.endswith('\n'):
|
||||
msg = msg[:-1]
|
||||
mnode = Message(message=msg)
|
||||
convert = lambda x: name if tools.to_bool(x) else Unstorable
|
||||
pnode = StoreNonEmpty(XONTRIB_PROMPT, converter=convert,
|
||||
path=_xontrib_path)
|
||||
return mnode, pnode
|
||||
|
||||
|
||||
def make_xontribs():
|
||||
"""Makes a xontrib wizard."""
|
||||
md = xontrib_metadata()
|
||||
pkgs = [md['packages'].get(d.get('package', None), {}) for d in md['xontribs']]
|
||||
w = _make_flat_wiz(make_xontrib, md['xontribs'], pkgs)
|
||||
return w
|
||||
|
||||
|
||||
def make_wizard(default_file=None, confirm=False):
|
||||
"""Makes a configuration wizard for xonsh config file.
|
||||
|
||||
|
@ -200,6 +265,8 @@ def make_wizard(default_file=None, confirm=False):
|
|||
make_fs(),
|
||||
Message(message=WIZARD_ENV),
|
||||
YesNo(question=WIZARD_ENV_QUESTION, yes=make_env(), no=Pass()),
|
||||
Message(message=WIZARD_XONTRIB),
|
||||
YesNo(question=WIZARD_XONTRIB_QUESTION, yes=make_xontribs(), no=Pass()),
|
||||
Message(message='\n' + HR + '\n'),
|
||||
Save(default_file=default_file, check=True),
|
||||
Message(message=WIZARD_TAIL),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"package": "xonsh",
|
||||
"url": "http://xon.sh",
|
||||
"description": ["Matplotlib hooks for xonsh, including the new 'mpl' alias ",
|
||||
"that dumps the current figure to the screen."]
|
||||
"that displays the current figure on the screen."]
|
||||
}
|
||||
],
|
||||
"packages": {
|
||||
|
|
Loading…
Add table
Reference in a new issue