mirror of
				https://github.com/powerline/powerline.git
				synced 2025-11-03 21:15:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			203 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# vim:fileencoding=utf-8:noet
 | 
						||
 | 
						||
from powerline.theme import Theme
 | 
						||
from unicodedata import east_asian_width, combining
 | 
						||
import os
 | 
						||
 | 
						||
 | 
						||
try:
 | 
						||
	NBSP = unicode(' ', 'utf-8')
 | 
						||
except NameError:
 | 
						||
	NBSP = ' '
 | 
						||
 | 
						||
 | 
						||
def construct_returned_value(rendered_highlighted, segments, output_raw):
 | 
						||
	if output_raw:
 | 
						||
		return rendered_highlighted, ''.join((segment['_rendered_raw'] for segment in segments))
 | 
						||
	else:
 | 
						||
		return rendered_highlighted
 | 
						||
 | 
						||
 | 
						||
class Renderer(object):
 | 
						||
	segment_info = {
 | 
						||
		'environ': os.environ,
 | 
						||
		'getcwd': getattr(os, 'getcwdu', os.getcwd),
 | 
						||
		'home': os.environ.get('HOME'),
 | 
						||
	}
 | 
						||
 | 
						||
	def __init__(self,
 | 
						||
				theme_config,
 | 
						||
				local_themes,
 | 
						||
				theme_kwargs,
 | 
						||
				colorscheme,
 | 
						||
				pl,
 | 
						||
				**options):
 | 
						||
		self.__dict__.update(options)
 | 
						||
		self.theme_config = theme_config
 | 
						||
		theme_kwargs['pl'] = pl
 | 
						||
		self.pl = pl
 | 
						||
		self.theme = Theme(theme_config=theme_config, **theme_kwargs)
 | 
						||
		self.local_themes = local_themes
 | 
						||
		self.theme_kwargs = theme_kwargs
 | 
						||
		self.colorscheme = colorscheme
 | 
						||
		self.width_data = {
 | 
						||
				'N': 1,                              # Neutral
 | 
						||
				'Na': 1,                             # Narrow
 | 
						||
				'A': getattr(self, 'ambiwidth', 1),  # Ambigious
 | 
						||
				'H': 1,                              # Half-width
 | 
						||
				'W': 2,                              # Wide
 | 
						||
				'F': 2,                              # Fullwidth
 | 
						||
				}
 | 
						||
 | 
						||
	def strwidth(self, string):
 | 
						||
		return sum((0 if combining(symbol) else self.width_data[east_asian_width(symbol)] for symbol in string))
 | 
						||
 | 
						||
	def get_theme(self, matcher_info):
 | 
						||
		return self.theme
 | 
						||
 | 
						||
	def shutdown(self):
 | 
						||
		self.theme.shutdown()
 | 
						||
 | 
						||
	def get_highlighting(self, segment, mode):
 | 
						||
		segment['highlight'] = self.colorscheme.get_highlighting(segment['highlight_group'], mode, segment.get('gradient_level'))
 | 
						||
		if segment['divider_highlight_group']:
 | 
						||
			segment['divider_highlight'] = self.colorscheme.get_highlighting(segment['divider_highlight_group'], mode)
 | 
						||
		else:
 | 
						||
			segment['divider_highlight'] = None
 | 
						||
		return segment
 | 
						||
 | 
						||
	def get_segment_info(self, segment_info):
 | 
						||
		return segment_info or self.segment_info
 | 
						||
 | 
						||
	def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None):
 | 
						||
		'''Render all segments.
 | 
						||
 | 
						||
		When a width is provided, low-priority segments are dropped one at
 | 
						||
		a time until the line is shorter than the width, or only segments
 | 
						||
		with a negative priority are left. If one or more filler segments are
 | 
						||
		provided they will fill the remaining space until the desired width is
 | 
						||
		reached.
 | 
						||
		'''
 | 
						||
		theme = self.get_theme(matcher_info)
 | 
						||
		segments = theme.get_segments(side, self.get_segment_info(segment_info))
 | 
						||
 | 
						||
		# Handle excluded/included segments for the current mode
 | 
						||
		segments = [self.get_highlighting(segment, mode) for segment in segments
 | 
						||
			if mode not in segment['exclude_modes'] or (segment['include_modes'] and segment in segment['include_modes'])]
 | 
						||
 | 
						||
		segments = [segment for segment in self._render_segments(theme, segments)]
 | 
						||
 | 
						||
		if not width:
 | 
						||
			# No width specified, so we don't need to crop or pad anything
 | 
						||
			return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw)
 | 
						||
 | 
						||
		# Create an ordered list of segments that can be dropped
 | 
						||
		segments_priority = [segment for segment in sorted(segments, key=lambda segment: segment['priority'], reverse=True) if segment['priority'] > 0]
 | 
						||
		while sum([segment['_len'] for segment in segments]) > width and len(segments_priority):
 | 
						||
			segments.remove(segments_priority[0])
 | 
						||
			segments_priority.pop(0)
 | 
						||
 | 
						||
		# Distribute the remaining space on spacer segments
 | 
						||
		segments_spacers = [segment for segment in segments if segment['width'] == 'auto']
 | 
						||
		if segments_spacers:
 | 
						||
			distribute_len, distribute_len_remainder = divmod(width - sum([segment['_len'] for segment in segments]), len(segments_spacers))
 | 
						||
			for segment in segments_spacers:
 | 
						||
				if segment['align'] == 'l':
 | 
						||
					segment['_space_right'] += distribute_len
 | 
						||
				elif segment['align'] == 'r':
 | 
						||
					segment['_space_left'] += distribute_len
 | 
						||
				elif segment['align'] == 'c':
 | 
						||
					space_side, space_side_remainder = divmod(distribute_len, 2)
 | 
						||
					segment['_space_left'] += space_side + space_side_remainder
 | 
						||
					segment['_space_right'] += space_side
 | 
						||
			segments_spacers[0]['_space_right'] += distribute_len_remainder
 | 
						||
 | 
						||
		rendered_highlighted = ''.join([segment['_rendered_hl'] for segment in self._render_segments(theme, segments)]) + self.hlstyle()
 | 
						||
 | 
						||
		return construct_returned_value(rendered_highlighted, segments, output_raw)
 | 
						||
 | 
						||
	def _render_segments(self, theme, segments, render_highlighted=True):
 | 
						||
		'''Internal segment rendering method.
 | 
						||
 | 
						||
		This method loops through the segment array and compares the
 | 
						||
		foreground/background colors and divider properties and returns the
 | 
						||
		rendered statusline as a string.
 | 
						||
 | 
						||
		The method always renders the raw segment contents (i.e. without
 | 
						||
		highlighting strings added), and only renders the highlighted
 | 
						||
		statusline if render_highlighted is True.
 | 
						||
		'''
 | 
						||
		segments_len = len(segments)
 | 
						||
 | 
						||
		for index, segment in enumerate(segments):
 | 
						||
			segment['_rendered_raw'] = ''
 | 
						||
			segment['_rendered_hl'] = ''
 | 
						||
 | 
						||
			prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT
 | 
						||
			next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT
 | 
						||
			compare_segment = next_segment if segment['side'] == 'left' else prev_segment
 | 
						||
			outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else ''
 | 
						||
			divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
 | 
						||
 | 
						||
			divider_raw = theme.get_divider(segment['side'], divider_type)
 | 
						||
			divider_spaces = theme.get_spaces()
 | 
						||
			divider_highlighted = ''
 | 
						||
			contents_raw = segment['contents']
 | 
						||
			contents_highlighted = ''
 | 
						||
 | 
						||
			# Pad segments first
 | 
						||
			if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'):
 | 
						||
				if segment['side'] == 'left':
 | 
						||
					contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + ((divider_spaces + segment['_space_right']) * ' ')
 | 
						||
				else:
 | 
						||
					contents_raw = ((divider_spaces + segment['_space_left']) * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding
 | 
						||
			else:
 | 
						||
				if segment['side'] == 'left':
 | 
						||
					contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ')
 | 
						||
				else:
 | 
						||
					contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding
 | 
						||
 | 
						||
			# Replace spaces with no-break spaces
 | 
						||
			contents_raw = contents_raw.replace(' ', NBSP)
 | 
						||
			divider_raw = divider_raw.replace(' ', NBSP)
 | 
						||
 | 
						||
			# Apply highlighting to padded dividers and contents
 | 
						||
			if render_highlighted:
 | 
						||
				if divider_type == 'soft':
 | 
						||
					divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight'
 | 
						||
					divider_fg = segment[divider_highlight_group_key]['fg']
 | 
						||
					divider_bg = segment[divider_highlight_group_key]['bg']
 | 
						||
				else:
 | 
						||
					divider_fg = segment['highlight']['bg']
 | 
						||
					divider_bg = compare_segment['highlight']['bg']
 | 
						||
				divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False)
 | 
						||
				contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
 | 
						||
 | 
						||
			# Append padded raw and highlighted segments to the rendered segment variables
 | 
						||
			if segment['draw_divider'] or (divider_type == 'hard' and segment['width'] != 'auto'):
 | 
						||
				if segment['side'] == 'left':
 | 
						||
					segment['_rendered_raw'] += contents_raw + divider_raw
 | 
						||
					segment['_rendered_hl'] += contents_highlighted + divider_highlighted
 | 
						||
				else:
 | 
						||
					segment['_rendered_raw'] += divider_raw + contents_raw
 | 
						||
					segment['_rendered_hl'] += divider_highlighted + contents_highlighted
 | 
						||
			else:
 | 
						||
				if segment['side'] == 'left':
 | 
						||
					segment['_rendered_raw'] += contents_raw
 | 
						||
					segment['_rendered_hl'] += contents_highlighted
 | 
						||
				else:
 | 
						||
					segment['_rendered_raw'] += contents_raw
 | 
						||
					segment['_rendered_hl'] += contents_highlighted
 | 
						||
			segment['_len'] = self.strwidth(segment['_rendered_raw'])
 | 
						||
			yield segment
 | 
						||
 | 
						||
	@staticmethod
 | 
						||
	def escape(string):
 | 
						||
		return string
 | 
						||
 | 
						||
	def hlstyle(fg=None, bg=None, attr=None):
 | 
						||
		raise NotImplementedError
 | 
						||
 | 
						||
	def hl(self, contents, fg=None, bg=None, attr=None):
 | 
						||
		return self.hlstyle(fg, bg, attr) + (contents or '')
 |