summaryrefslogtreecommitdiffstats
path: root/powerline/renderers/vim.py
blob: a92d51c261e301495304bafe084227a594002921 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)

import sys

import vim

from powerline.bindings.vim import vim_get_func, vim_getoption, environ, current_tabpage, get_vim_encoding
from powerline.renderer import Renderer
from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE
from powerline.theme import Theme
from powerline.lib.unicode import unichr, register_strwidth_error


vim_mode = vim_get_func('mode', rettype='unicode')
if int(vim.eval('v:version')) >= 702:
	_vim_mode = vim_mode
	vim_mode = lambda: _vim_mode(1)

mode_translations = {
	unichr(ord('V') - 0x40): '^V',
	unichr(ord('S') - 0x40): '^S',
}


class VimRenderer(Renderer):
	'''Powerline vim segment renderer.'''

	character_translations = Renderer.character_translations.copy()
	character_translations[ord('%')] = '%%'

	segment_info = Renderer.segment_info.copy()
	segment_info.update(environ=environ)

	def __init__(self, *args, **kwargs):
		if not hasattr(vim, 'strwidth'):
			# Hope nobody want to change this at runtime
			if vim.eval('&ambiwidth') == 'double':
				kwargs = dict(**kwargs)
				kwargs['ambigious'] = 2
		super(VimRenderer, self).__init__(*args, **kwargs)
		self.hl_groups = {}
		self.prev_highlight = None
		self.strwidth_error_name = register_strwidth_error(self.strwidth)
		self.encoding = get_vim_encoding()

	def shutdown(self):
		self.theme.shutdown()
		for match in self.local_themes.values():
			if 'theme' in match:
				match['theme'].shutdown()

	def add_local_theme(self, matcher, theme):
		if matcher in self.local_themes:
			raise KeyError('There is already a local theme with given matcher')
		self.local_themes[matcher] = theme

	def get_matched_theme(self, match):
		try:
			return match['theme']
		except KeyError:
			match['theme'] = Theme(theme_config=match['config'], main_theme_config=self.theme_config, **self.theme_kwargs)
			return match['theme']

	def get_theme(self, matcher_info):
		if matcher_info is None:
			return self.get_matched_theme(self.local_themes[None])
		for matcher in self.local_themes.keys():
			if matcher and matcher(matcher_info):
				return self.get_matched_theme(self.local_themes[matcher])
		else:
			return self.theme

	if hasattr(vim, 'strwidth'):
		if sys.version_info < (3,):
			def strwidth(self, string):
				# Does not work with tabs, but neither is strwidth from default 
				# renderer
				return vim.strwidth(string.encode(self.encoding, 'replace'))
		else:
			@staticmethod
			def strwidth(string):
				return vim.strwidth(string)

	def get_segment_info(self, segment_info, mode):
		return segment_info or self.segment_info

	def render(self, window=None, window_id=None, winnr=None, is_tabline=False):
		'''Render all segments.'''
		segment_info = self.segment_info.copy()

		if window is vim.current.window:
			mode = vim_mode()
			mode = mode_translations.get(mode, mode)
		else:
			mode = 'nc'

		segment_info.update(
			window=window,
			mode=mode,
			window_id=window_id,
			winnr=winnr,
			buffer=window.buffer,
			tabpage=current_tabpage(),
			encoding=self.encoding,
		)
		segment_info['tabnr'] = segment_info['tabpage'].number
		segment_info['bufnr'] = segment_info['buffer'].number
		if is_tabline:
			winwidth = int(vim_getoption('columns'))
		else:
			winwidth = segment_info['window'].width

		statusline = super(VimRenderer, self).render(
			mode=mode,
			width=winwidth,
			segment_info=segment_info,
			matcher_info=(None if is_tabline else segment_info),
		)
		statusline = statusline.encode(self.encoding, self.strwidth_error_name)
		return statusline

	def reset_highlight(self):
		self.hl_groups.clear()

	def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs):
		'''Highlight a segment.

		If an argument is None, the argument is ignored. If an argument is
		False, the argument is reset to the terminal defaults. If an argument
		is a valid color or attribute, it’s added to the vim highlight group.
		'''
		# In order not to hit E541 two consequent identical highlighting 
		# specifiers may be squashed into one.
		attrs = attrs or 0  # Normalize `attrs`
		if (fg, bg, attrs) == self.prev_highlight:
			return ''
		self.prev_highlight = (fg, bg, attrs)

		# We don’t need to explicitly reset attributes in vim, so skip those 
		# calls
		if not attrs and not bg and not fg:
			return ''

		if not (fg, bg, attrs) in self.hl_groups:
			hl_group = {
				'ctermfg': 'NONE',
				'guifg': None,
				'ctermbg': 'NONE',
				'guibg': None,
				'attrs': ['NONE'],
				'name': '',
			}
			if fg is not None and fg is not False:
				hl_group['ctermfg'] = fg[0]
				hl_group['guifg'] = fg[1]
			if bg is not None and bg is not False:
				hl_group['ctermbg'] = bg[0]
				hl_group['guibg'] = bg[1]
			if attrs:
				hl_group['attrs'] = []
				if attrs & ATTR_BOLD:
					hl_group['attrs'].append('bold')
				if attrs & ATTR_ITALIC:
					hl_group['attrs'].append('italic')
				if attrs & ATTR_UNDERLINE:
					hl_group['attrs'].append('underline')
			hl_group['name'] = (
				'Pl_'
				+ str(hl_group['ctermfg']) + '_'
				+ str(hl_group['guifg']) + '_'
				+ str(hl_group['ctermbg']) + '_'
				+ str(hl_group['guibg']) + '_'
				+ ''.join(hl_group['attrs'])
			)
			self.hl_groups[(fg, bg, attrs)] = hl_group
			vim.command('hi {group} ctermfg={ctermfg} guifg={guifg} guibg={guibg} ctermbg={ctermbg} cterm={attrs} gui={attrs}'.format(
				group=hl_group['name'],
				ctermfg=hl_group['ctermfg'],
				guifg='#{0:06x}'.format(hl_group['guifg']) if hl_group['guifg'] is not None else 'NONE',
				ctermbg=hl_group['ctermbg'],
				guibg='#{0:06x}'.format(hl_group['guibg']) if hl_group['guibg'] is not None else 'NONE',
				attrs=','.join(hl_group['attrs']),
			))
		return '%#' + self.hl_groups[(fg, bg, attrs)]['name'] + '#'


renderer = VimRenderer