summaryrefslogtreecommitdiffstats
path: root/powerline/commands/main.py
blob: 373225fca85660bf521410d020fa31a80a454be2 (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
189
190
# vim:fileencoding=utf-8:noet
# WARNING: using unicode_literals causes errors in argparse
from __future__ import (division, absolute_import, print_function)

import argparse
import sys

from itertools import chain

from powerline.lib.overrides import parsedotval, parse_override_var
from powerline.lib.dict import mergeargs
from powerline.lib.encoding import get_preferred_arguments_encoding
from powerline.lib.unicode import u, unicode
from powerline.bindings.wm import wm_threads


if sys.version_info < (3,):
	encoding = get_preferred_arguments_encoding()

	def arg_to_unicode(s):
		return unicode(s, encoding, 'replace') if not isinstance(s, unicode) else s  # NOQA
else:
	def arg_to_unicode(s):
		return s


def finish_args(parser, environ, args, is_daemon=False):
	'''Do some final transformations

	Transforms ``*_override`` arguments into dictionaries, adding overrides from 
	environment variables. Transforms ``renderer_arg`` argument into dictionary 
	as well, but only if it is true.

	:param dict environ:
		Environment from which additional overrides should be taken from.
	:param args:
		Arguments object returned by 
		:py:meth:`argparse.ArgumentParser.parse_args`. Will be modified 
		in-place.

	:return: Object received as second (``args``) argument.
	'''
	args.config_override = mergeargs(chain(
		parse_override_var(environ.get('POWERLINE_CONFIG_OVERRIDES', '')),
		(parsedotval(v) for v in args.config_override or ()),
	))
	args.theme_override = mergeargs(chain(
		parse_override_var(environ.get('POWERLINE_THEME_OVERRIDES', '')),
		(parsedotval(v) for v in args.theme_override or ()),
	))
	if args.renderer_arg:
		args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg), remove=True)
		if 'pane_id' in args.renderer_arg:
			if isinstance(args.renderer_arg['pane_id'], (bytes, unicode)):
				try:
					args.renderer_arg['pane_id'] = int(args.renderer_arg['pane_id'].lstrip(' %'))
				except ValueError:
					pass
			if 'client_id' not in args.renderer_arg:
				args.renderer_arg['client_id'] = args.renderer_arg['pane_id']
	args.config_path = (
		[path for path in environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path]
		+ (args.config_path or [])
	)
	if args.ext[0].startswith('wm.'):
		if not is_daemon:
			parser.error('WM bindings must be used with daemon only')
		elif args.ext[0][3:] not in wm_threads:
			parser.error('WM binding not found')
	elif not args.side:
		parser.error('expected one argument')
	return args


def int_or_sig(s):
	if s.startswith('sig'):
		return u(s)
	else:
		return int(s)


def get_argparser(ArgumentParser=argparse.ArgumentParser):
	parser = ArgumentParser(description='Powerline prompt and statusline script.')
	parser.add_argument(
		'ext', nargs=1,
		help='Extension: application for which powerline command is launched '
		     '(usually `shell\' or `tmux\'). Also supports `wm.\' extensions: '
		     + ', '.join(('`wm.' + key + '\'' for key in wm_threads.keys())) + '.'
	)
	parser.add_argument(
		'side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'),
		help='Side: `left\' and `right\' represent left and right side '
		     'respectively, `above\' emits lines that are supposed to be printed '
		     'just above the prompt and `aboveleft\' is like concatenating '
		     '`above\' with `left\' with the exception that only one Python '
		     'instance is used in this case. May be omitted for `wm.*\' extensions.'
	)
	parser.add_argument(
		'-r', '--renderer-module', metavar='MODULE', type=str,
		help='Renderer module. Usually something like `.bash\' or `.zsh\' '
		     '(with leading dot) which is `powerline.renderers.{ext}{MODULE}\', '
		     'may also be full module name (must contain at least one dot or '
		     'end with a dot in case it is top-level module) or '
		     '`powerline.renderers\' submodule (in case there are no dots).'
	)
	parser.add_argument(
		'-w', '--width', type=int,
		help='Maximum prompt with. Triggers truncation of some segments.'
	)
	parser.add_argument(
		'--last-exit-code', metavar='INT', type=int_or_sig,
		help='Last exit code.'
	)
	parser.add_argument(
		'--last-pipe-status', metavar='LIST', default='',
		type=lambda s: [int_or_sig(status) for status in s.split()],
		help='Like above, but is supposed to contain space-separated array '
		     'of statuses, representing exit statuses of commands in one pipe.'
	)
	parser.add_argument(
		'--jobnum', metavar='INT', type=int,
		help='Number of jobs.'
	)
	parser.add_argument(
		'-c', '--config-override', metavar='KEY.KEY=VALUE', type=arg_to_unicode,
		action='append',
		help='Configuration overrides for `config.json\'. Is translated to a '
		     'dictionary and merged with the dictionary obtained from actual '
		     'JSON configuration: KEY.KEY=VALUE is translated to '
		     '`{"KEY": {"KEY": VALUE}}\' and then merged recursively. '
		     'VALUE may be any JSON value, values that are not '
		     '`null\', `true\', `false\', start with digit, `{\', `[\' '
		     'are treated like strings. If VALUE is omitted '
		     'then corresponding key is removed.'
	)
	parser.add_argument(
		'-t', '--theme-override', metavar='THEME.KEY.KEY=VALUE', type=arg_to_unicode,
		action='append',
		help='Like above, but theme-specific. THEME should point to '
		     'an existing and used theme to have any effect, but it is fine '
		     'to use any theme here.'
	)
	parser.add_argument(
		'-R', '--renderer-arg',
		metavar='KEY=VAL', type=arg_to_unicode, action='append',
		help='Like above, but provides argument for renderer. Is supposed '
		     'to be used only by shell bindings to provide various data like '
		     'last-exit-code or last-pipe-status (they are not using '
		     '`--renderer-arg\' for historical reasons: `--renderer-arg\' '
		     'was added later).'
	)
	parser.add_argument(
		'-p', '--config-path', action='append', metavar='PATH',
		help='Path to configuration directory. If it is present then '
		     'configuration files will only be sought in the provided path. '
		     'May be provided multiple times to search in a list of directories.'
	)
	parser.add_argument(
		'--socket', metavar='ADDRESS', type=str,
		help='Socket address to use in daemon clients. Is always UNIX domain '
		     'socket on linux and file socket on Mac OS X. Not used here, '
		     'present only for compatibility with other powerline clients. '
		     'This argument must always be the first one and be in a form '
		     '`--socket ADDRESS\': no `=\' or short form allowed '
		     '(in other powerline clients, not here).'
	)
	return parser


def write_output(args, powerline, segment_info, write):
	if args.renderer_arg:
		segment_info.update(args.renderer_arg)
	if args.side.startswith('above'):
		for line in powerline.render_above_lines(
			width=args.width,
			segment_info=segment_info,
			mode=segment_info.get('mode', None),
		):
			if line:
				write(line + '\n')
		args.side = args.side[len('above'):]

	if args.side:
		rendered = powerline.render(
			width=args.width,
			side=args.side,
			segment_info=segment_info,
			mode=segment_info.get('mode', None),
		)
		write(rendered)