Merge remote-tracking branch 'zyx-i/ipython-other' into develop

This commit is contained in:
Kim Silkebækken 2013-03-24 16:18:26 +01:00
commit 154fe123fe
21 changed files with 275 additions and 77 deletions

View File

@ -57,7 +57,6 @@ class Powerline(object):
theme_kwargs = {
'ext': ext,
'common_config': common_config,
'segment_info': self.get_segment_info(),
'run_once': run_once,
}
local_themes = self.get_local_themes(ext_config.get('local_themes'))
@ -143,14 +142,3 @@ class Powerline(object):
``__init__`` arguments, refer to its documentation.
'''
return None
@staticmethod
def get_segment_info():
'''Get information for segments that require ``segment_info`` argument.
To be overridden in subclasses.
:return:
anything accepted by segments as ``segment_info`` argument. Depends
on the segments used, refer to Powerline subclass documentation.
'''
return None

View File

@ -5,17 +5,26 @@ from IPython.core.prompts import PromptManager
from IPython.core.hooks import TryNext
class IpythonInfo(object):
def __init__(self, shell):
self._shell = shell
@property
def prompt_count(self):
return self._shell.execution_count
class PowerlinePromptManager(PromptManager):
powerline = None
def __init__(self, powerline, **kwargs):
def __init__(self, powerline, shell):
self.powerline = powerline
super(PowerlinePromptManager, self).__init__(**kwargs)
self.powerline_segment_info = IpythonInfo(shell)
self.shell = shell
def render(self, name, color=True, *args, **kwargs):
if name != 'in':
return super(PowerlinePromptManager, self).render(name, color, *args, **kwargs)
res, res_nocolor = self.powerline.renderer.render(output_raw=True)
width = None if name == 'in' else self.width
res, res_nocolor = self.powerline.renderer.render(output_raw=True, width=width, matcher_info=name, segment_info=self.powerline_segment_info)
self.txtwidth = len(res_nocolor)
self.width = self.txtwidth
return res if color else res_nocolor
@ -39,8 +48,7 @@ def load_ipython_extension(ip):
old_prompt_manager = ip.prompt_manager
powerline = ConfigurableIpythonPowerline(ip)
ip.prompt_manager = PowerlinePromptManager(powerline=powerline,
shell=ip.prompt_manager.shell, config=ip.prompt_manager.config)
ip.prompt_manager = PowerlinePromptManager(powerline=powerline, shell=ip.prompt_manager.shell)
def shutdown_hook():
powerline.renderer.shutdown()

View File

@ -1,23 +1,105 @@
# vim:fileencoding=utf-8:noet
from powerline.ipython import IpythonPowerline
from IPython.Prompts import BasePrompt
from IPython.Prompts import BasePrompt, Prompt1
from IPython.ipapi import get as get_ipython
from IPython.ipapi import TryNext
import re
def string(s):
if type(s) is not str:
return s.encode('utf-8')
else:
return s
# HACK: ipython tries to only leave us with plain ASCII
class RewriteResult(object):
def __init__(self, prompt):
self.prompt = string(prompt)
def __str__(self):
return self.prompt
def __add__(self, s):
if type(s) is not str:
try:
s = s.encode('utf-8')
except AttributeError:
raise NotImplementedError
return RewriteResult(self.prompt + s)
class IpythonInfo(object):
def __init__(self, cache):
self._cache = cache
@property
def prompt_count(self):
return self._cache.prompt_count
class PowerlinePrompt(BasePrompt):
def __init__(self, powerline, *args, **kwargs):
def __init__(self, powerline, powerline_last_in, old_prompt):
self.powerline = powerline
super(PowerlinePrompt, self).__init__(*args, **kwargs)
self.powerline_last_in = powerline_last_in
self.powerline_segment_info = IpythonInfo(old_prompt.cache)
self.cache = old_prompt.cache
if hasattr(old_prompt, 'sep'):
self.sep = old_prompt.sep
self.pad_left = False
def __str__(self):
self.set_p_str()
return string(self.p_str)
def set_p_str(self, width=None):
self.p_str, self.p_str_nocolor = (
self.powerline.renderer.render(output_raw=True,
segment_info=self.powerline_segment_info,
matcher_info=self.powerline_prompt_type,
width=width)
)
@staticmethod
def set_colors():
pass
class PowerlinePrompt1(PowerlinePrompt):
powerline_prompt_type = 'in'
rspace = re.compile(r'(\s*)$')
def __str__(self):
self.cache.prompt_count += 1
self.set_p_str()
self.cache.last_prompt = self.p_str_nocolor.split('\n')[-1]
return string(self.p_str)
def set_p_str(self):
self.p_str, self.p_str_nocolor = self.powerline.renderer.render(output_raw=True)
super(PowerlinePrompt1, self).set_p_str()
self.nrspaces = len(self.rspace.search(self.p_str_nocolor).group())
self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces - 1
self.prompt_text_len = len(self.p_str_nocolor) - self.nrspaces
self.powerline_last_in['nrspaces'] = self.nrspaces
self.powerline_last_in['prompt_text_len'] = self.prompt_text_len
def auto_rewrite(self):
# TODO color this
return '%s>%s' % ('-' * self.prompt_text_len, ' ' * self.nrspaces)
return RewriteResult(self.powerline.renderer.render(matcher_info='rewrite', width=self.prompt_text_len, segment_info=self.powerline_segment_info)
+ (' ' * self.nrspaces))
class PowerlinePromptOut(PowerlinePrompt):
powerline_prompt_type = 'out'
def set_p_str(self):
super(PowerlinePromptOut, self).set_p_str(width=self.powerline_last_in['prompt_text_len'])
spaces = ' ' * self.powerline_last_in['nrspaces']
self.p_str += spaces
self.p_str_nocolor += spaces
class PowerlinePrompt2(PowerlinePromptOut):
powerline_prompt_type = 'in2'
class ConfigurableIpythonPowerline(IpythonPowerline):
@ -28,17 +110,20 @@ class ConfigurableIpythonPowerline(IpythonPowerline):
super(ConfigurableIpythonPowerline, self).__init__()
def setup(prompt='1', **kwargs):
def setup(**kwargs):
ip = get_ipython()
powerline = ConfigurableIpythonPowerline(**kwargs)
attr = 'prompt' + prompt
def late_startup_hook():
old_prompt = getattr(ip.IP.outputcache, attr)
setattr(ip.IP.outputcache, attr, PowerlinePrompt(powerline,
old_prompt.cache, old_prompt.sep, '', old_prompt.pad_left))
last_in = {'nrspaces': 0, 'prompt_text_len': None}
for attr, prompt_class in (
('prompt1', PowerlinePrompt1),
('prompt2', PowerlinePrompt2),
('prompt_out', PowerlinePromptOut)
):
old_prompt = getattr(ip.IP.outputcache, attr)
setattr(ip.IP.outputcache, attr, prompt_class(powerline, last_in, old_prompt))
raise TryNext()
def shutdown_hook():

View File

@ -2,6 +2,7 @@
"name": "Default color scheme for IPython prompt",
"groups": {
"virtualenv": { "fg": "white", "bg": "darkcyan" },
"prompt": { "fg": "gray9", "bg": "gray4" }
"prompt": { "fg": "gray9", "bg": "gray4" },
"prompt_count": { "fg": "white", "bg": "gray4" }
}
}

View File

@ -16,7 +16,12 @@
"ext": {
"ipython": {
"colorscheme": "default",
"theme": "default"
"theme": "in",
"local_themes": {
"rewrite": "rewrite",
"out": "out",
"in2": "in2"
}
},
"shell": {
"colorscheme": "default",

View File

@ -1,16 +0,0 @@
{
"default_module": "powerline.segments.common",
"segments": {
"left": [
{
"name": "virtualenv"
},
{
"type": "string",
"contents": "In:",
"highlight_group": ["prompt"],
"draw_divider": false
}
]
}
}

View File

@ -0,0 +1,26 @@
{
"default_module": "powerline.segments.common",
"segments": {
"left": [
{
"name": "virtualenv"
},
{
"type": "string",
"contents": "In[",
"draw_divider": false,
"highlight_group": ["prompt"]
},
{
"name": "prompt_count",
"module": "powerline.segments.ipython",
"draw_divider": false
},
{
"type": "string",
"contents": "]",
"highlight_group": ["prompt"]
}
]
}
}

View File

@ -0,0 +1,13 @@
{
"default_module": "powerline.segments.common",
"segments": {
"left": [
{
"type": "string",
"contents": "",
"width": "auto",
"highlight_group": ["prompt"]
}
]
}
}

View File

@ -0,0 +1,25 @@
{
"default_module": "powerline.segments.common",
"segments": {
"left": [
{
"type": "string",
"contents": "Out[",
"draw_divider": false,
"width": "auto",
"align": "r",
"highlight_group": ["prompt"]
},
{
"name": "prompt_count",
"module": "powerline.segments.ipython",
"draw_divider": false
},
{
"type": "string",
"contents": "]",
"highlight_group": ["prompt"]
}
]
}
}

View File

@ -0,0 +1,23 @@
{
"segments": {
"left": [
{
"type": "string",
"contents": "",
"draw_divider": false,
"width": "auto",
"highlight_group": ["prompt"]
},
{
"name": "prompt_count",
"module": "powerline.segments.ipython",
"draw_divider": false
},
{
"type": "string",
"contents": ">",
"highlight_group": ["prompt"]
}
]
}
}

View File

@ -14,6 +14,9 @@ class IpythonPowerline(Powerline):
else:
return super(IpythonPowerline, self).get_config_paths()
def get_local_themes(self, local_themes):
return dict(((type, {'config': self.load_theme_config(name)}) for type, name in local_themes.items()))
def load_main_config(self):
r = super(IpythonPowerline, self).load_main_config()
if self.config_overrides:

View File

@ -84,7 +84,7 @@ class Spec(object):
if type(value.value) is not t:
echoerr(context=self.cmsg.format(key=context_key(context)),
context_mark=context_mark,
problem='must be a {0} instance'.format(t.__name__),
problem='{0!r} must be a {1} instance, not {2}'.format(value, t.__name__, type(value.value).__name__),
problem_mark=value.mark)
return False, True
return True, False
@ -380,9 +380,9 @@ def check_config(d, theme, data, context, echoerr):
else:
# local_themes
ext = context[-3][0]
if ext not in data['configs']['themes'] or theme not in data['configs']['themes'][ext]:
if ext not in data['configs'][d] or theme not in data['configs'][d][ext]:
echoerr(context='Error while loading {0} from {1} extension configuration'.format(d[:-1], ext),
problem='failed to find configuration file themes/{0}/{1}.json'.format(ext, theme),
problem='failed to find configuration file {0}/{1}/{2}.json'.format(d, ext, theme),
problem_mark=theme.mark)
return True, False, True
return True, False, False
@ -416,6 +416,15 @@ main_spec = (Spec(
local_themes=Spec()
.unknown_spec(lambda *args: check_matcher_func('vim', *args), theme_spec())
),
ipython=Spec(
colorscheme=colorscheme_spec(),
theme=theme_spec(),
local_themes=Spec(
in2=theme_spec(),
out=theme_spec(),
rewrite=theme_spec(),
),
),
).unknown_spec(check_ext,
Spec(
colorscheme=colorscheme_spec(),

View File

@ -24,6 +24,12 @@ class ScannerError(MarkedError):
pass
try:
from __builtin__ import unicode
except ImportError:
unicode = str
class SimpleKey:
# See below simple keys treatment.
def __init__(self, token_number, index, line, column, mark):
@ -379,7 +385,7 @@ class Scanner:
chunks.extend(self.scan_flow_scalar_non_spaces(start_mark))
self.forward()
end_mark = self.get_mark()
return ScalarToken(''.join(chunks), False, start_mark, end_mark, '"')
return ScalarToken(unicode().join(chunks), False, start_mark, end_mark, '"')
ESCAPE_REPLACEMENTS = {
'b': '\x08',

View File

@ -61,10 +61,7 @@ class Renderer(object):
reached.
'''
theme = self.get_theme(matcher_info)
segments = theme.get_segments(side)
if segment_info:
theme.segment_info.update(segment_info)
segments = theme.get_segments(side, segment_info)
# Handle excluded/included segments for the current mode
segments = [self.get_highlighting(segment, mode) for segment in segments

View File

@ -1,6 +1,7 @@
# vim:fileencoding=utf-8:noet
from powerline.renderers.shell import ShellRenderer
from powerline.theme import Theme
class IpythonRenderer(ShellRenderer):
@ -8,5 +9,16 @@ class IpythonRenderer(ShellRenderer):
escape_hl_start = '\x01'
escape_hl_end = '\x02'
def get_theme(self, matcher_info):
if matcher_info == 'in':
return self.theme
else:
match = self.local_themes[matcher_info]
try:
return match['theme']
except KeyError:
match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs)
return match['theme']
renderer = IpythonRenderer

View File

@ -26,7 +26,7 @@ class ShellRenderer(Renderer):
False, the argument is reset to the terminal defaults. If an argument
is a valid color or attribute, it's added to the ANSI escape code.
'''
ansi = []
ansi = [0]
if fg is not None:
if fg is False or fg[0] is False:
ansi += [39]

View File

@ -46,9 +46,11 @@ class VimRenderer(Renderer):
for matcher in self.local_themes.keys():
if matcher(matcher_info):
match = self.local_themes[matcher]
if 'config' in match:
match['theme'] = Theme(theme_config=match.pop('config'), top_theme_config=self.theme_config, **self.theme_kwargs)
return match['theme']
try:
return match['theme']
except KeyError:
match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs)
return match['theme']
else:
return self.theme

View File

@ -0,0 +1,7 @@
# vim:fileencoding=utf-8:noet
from powerline.theme import requires_segment_info
@requires_segment_info
def prompt_count(segment_info):
return str(segment_info.prompt_count)

View File

@ -24,7 +24,7 @@ def requires_segment_info(func):
class Theme(object):
def __init__(self, ext, theme_config, common_config, top_theme_config=None, segment_info=None, run_once=False):
def __init__(self, ext, theme_config, common_config, top_theme_config=None, run_once=False):
self.dividers = theme_config.get('dividers', common_config['dividers'])
self.spaces = theme_config.get('spaces', common_config['spaces'])
self.segments = {
@ -35,7 +35,6 @@ class Theme(object):
'contents': None,
'highlight': {'fg': False, 'bg': False, 'attr': 0}
}
self.segment_info = segment_info
theme_configs = [theme_config]
if top_theme_config:
theme_configs.append(top_theme_config)
@ -62,7 +61,7 @@ class Theme(object):
def get_spaces(self):
return self.spaces
def get_segments(self, side=None):
def get_segments(self, side=None, segment_info=None):
'''Return all segments.
Function segments are called, and all segments get their before/after
@ -74,7 +73,7 @@ class Theme(object):
if segment['type'] == 'function':
if (hasattr(segment['contents_func'], 'powerline_requires_segment_info')
and segment['contents_func'].powerline_requires_segment_info):
contents = segment['contents_func'](segment_info=self.segment_info, **segment['args'])
contents = segment['contents_func'](segment_info=segment_info, **segment['args'])
else:
contents = segment['contents_func'](**segment['args'])
if contents is None:

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
'''Powerline prompt and statusline script.'''
import sys
import os
try:
from powerline.shell import ShellPowerline, get_argparser
@ -13,7 +14,7 @@ except ImportError:
if __name__ == '__main__':
args = get_argparser(description=__doc__).parse_args()
powerline = ShellPowerline(args, run_once=True)
rendered = powerline.renderer.render(width=args.width, side=args.side)
rendered = powerline.renderer.render(width=args.width, side=args.side, segment_info=args)
try:
sys.stdout.write(rendered)
except UnicodeEncodeError:

View File

@ -76,18 +76,20 @@ class TestConfig(TestCase):
def test_zsh(self):
from powerline.shell import ShellPowerline
powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False)
powerline.renderer.render()
powerline = ShellPowerline(Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt'), run_once=False)
powerline.renderer.render()
args = Args(last_pipe_status=[1, 0], ext=['shell'], renderer_module='zsh_prompt')
powerline = ShellPowerline(args, run_once=False)
powerline.renderer.render(segment_info=args)
powerline = ShellPowerline(args, run_once=False)
powerline.renderer.render(segment_info=args)
shutdown(powerline)
def test_bash(self):
from powerline.shell import ShellPowerline
powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False)
powerline.renderer.render()
powerline = ShellPowerline(Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})]), run_once=False)
powerline.renderer.render()
args = Args(last_exit_code=1, ext=['shell'], renderer_module='bash_prompt', config=[('ext', {'shell': {'theme': 'default_leftonly'}})])
powerline = ShellPowerline(args, run_once=False)
powerline.renderer.render(segment_info=args)
powerline = ShellPowerline(args, run_once=False)
powerline.renderer.render(segment_info=args)
shutdown(powerline)
def test_ipython(self):
@ -99,8 +101,10 @@ class TestConfig(TestCase):
theme_overrides = {}
powerline = IpyPowerline()
powerline.renderer.render()
powerline.renderer.render()
segment_info = Args(prompt_count=1)
for prompt_type in ['in', 'in2', 'out', 'rewrite']:
powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info)
powerline.renderer.render(matcher_info=prompt_type, segment_info=segment_info)
shutdown(powerline)
def test_wm(self):