kitty/kittens/diff/render.py
Kovid Goyal d54b58d2f3
...
2018-04-25 19:50:40 +05:30

144 lines
4.1 KiB
Python

#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
from gettext import gettext as _
from kitty.fast_data_types import truncate_point_for_length
from .collect import data_for_path, lines_for_path, path_name_map
from .config import formats
class Reference:
__slots__ = ('path',)
def __init__(self, path):
self.path = path
class Line:
__slots__ = ('text', 'ref')
def __init__(self, text, ref):
self.text = text
self.ref = ref
def yield_lines_from(iterator, reference):
for text in iterator:
yield Line(text, reference)
def human_readable(size, sep=' '):
""" Convert a size in bytes into a human readable form """
divisor, suffix = 1, "B"
for i, candidate in enumerate(('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
if size < (1 << ((i + 1) * 10)):
divisor, suffix = (1 << (i * 10)), candidate
break
size = str(float(size)/divisor)
if size.find(".") > -1:
size = size[:size.find(".")+2]
if size.endswith('.0'):
size = size[:-2]
return size + sep + suffix
sanitize_pat = re.compile('[\x00-\x1f\x7f\x80-\x9f]')
def sanitize_sub(m):
return '<{:x}>'.format(ord(m.group()[0]))
def sanitize(text):
return sanitize_pat.sub(sanitize_sub, text)
def fit_in(text, count):
p = truncate_point_for_length(text, count)
if p >= len(text):
return text
if count > 1:
p = truncate_point_for_length(text, count - 1)
return text[:p] + ''
def place_in(text, sz):
return fit_in(text, sz).ljust(sz)
def format_func(which):
def formatted(text):
fmt = formats[which]
return '\x1b[' + fmt + 'm' + text + '\x1b[0m'
formatted.__name__ = which + '_format'
return formatted
text_format = format_func('text')
title_format = format_func('title')
margin_format = format_func('margin')
def title_lines(left_path, right_path, args, columns, margin_size):
name = fit_in(sanitize(path_name_map[left_path]), columns - 2 * margin_size)
yield title_format((' ' + name).ljust(columns))
yield title_format('' * columns)
yield title_format(' ' * columns)
def binary_lines(path, other_path, columns, margin_size):
template = _('Binary file: {}')
def fl(path):
text = template.format(human_readable(len(data_for_path(path))))
text = place_in(text, columns // 2 - margin_size)
return margin_format(' ' * margin_size) + text_format(text) + '\x1b[0m'
return fl(path) + fl(other_path)
def split_to_size(line, width):
while line:
p = truncate_point_for_length(line, width)
yield line[:p]
line = line[p:]
def lines_for_diff(left_path, right_path, hunks, args, columns, margin_size):
raise NotImplementedError('TODO')
lines_for_path()
# available_cols = columns // 2 - margin_size
#
# for hunk_num, hunk in enumerate(hunks):
# for line_num, (left, right) in enumerate(zip(hunk.left_lines, hunk.right_lines)):
# left_line_number, left_is_
# left_line = lines_for_path(left_path)[
# left_lines, right_lines = list(split_to_size(left_line, available_cols)), list(split_to_size(
def render_diff(collection, diff_map, args, columns):
largest_line_number = 0
for path, item_type, other_path in collection:
if item_type == 'diff':
patch = diff_map.get(path)
if patch is not None:
largest_line_number = max(largest_line_number, patch.largest_line_number)
margin_size = max(3, len(str(largest_line_number)) + 1)
for path, item_type, other_path in collection:
item_ref = Reference(path)
if item_type == 'diff':
yield from yield_lines_from(title_lines(path, other_path, args, columns, margin_size), item_ref)
is_binary = isinstance(data_for_path(path), bytes)
if is_binary:
yield from yield_lines_from(binary_lines(path, other_path, columns, margin_size), item_ref)
else:
yield from lines_for_diff(path, other_path, diff_map[path], args, columns, margin_size)