diff kitten: keyboard shortcuts to change the number of lines of context

This commit is contained in:
Kovid Goyal 2018-05-08 22:19:50 +05:30
parent 96793a296c
commit 324c223d54
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 78 additions and 23 deletions

View File

@ -4,6 +4,7 @@
import os import os
import sys import sys
from collections import defaultdict
from functools import partial from functools import partial
from gettext import gettext as _ from gettext import gettext as _
@ -17,7 +18,7 @@ from ..tui.loop import Loop
from .collect import create_collection, data_for_path, set_highlight_data from .collect import create_collection, data_for_path, set_highlight_data
from .config import init_config from .config import init_config
from .patch import Differ from .patch import Differ
from .render import render_diff from .render import LineRef, render_diff
try: try:
from .highlight import initialize_highlighter, highlight_collection from .highlight import initialize_highlighter, highlight_collection
@ -49,18 +50,47 @@ class DiffHandler(Handler):
self.report_traceback_on_exit = None self.report_traceback_on_exit = None
self.args = args self.args = args
self.scroll_pos = self.max_scroll_pos = 0 self.scroll_pos = self.max_scroll_pos = 0
self.current_context_count = self.args.context
self.highlighting_done = False self.highlighting_done = False
self.restore_position = None
def create_collection(self): def create_collection(self):
self.start_job('collect', create_collection, self.left, self.right) self.start_job('collect', create_collection, self.left, self.right)
def generate_diff(self): def generate_diff(self):
self.start_job('diff', generate_diff, self.collection, self.args.context) self.start_job('diff', generate_diff, self.collection, self.current_context_count)
def render_diff(self): def render_diff(self):
self.diff_lines = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols)) self.diff_lines = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols))
self.ref_path_map = defaultdict(list)
for i, l in enumerate(self.diff_lines):
self.ref_path_map[l.ref.path].append((i, l.ref))
self.max_scroll_pos = len(self.diff_lines) - self.num_lines self.max_scroll_pos = len(self.diff_lines) - self.num_lines
@property
def current_position(self):
return self.diff_lines[min(len(self.diff_lines) - 1, self.scroll_pos)].ref
@current_position.setter
def current_position(self, ref):
num = None
if isinstance(ref.extra, LineRef):
sln = ref.extra.src_line_number
for i, q in self.ref_path_map[ref.path]:
if isinstance(q.extra, LineRef):
if q.extra.src_line_number >= sln:
if q.extra.src_line_number == sln:
num = i
break
num = i
if num is None:
for i, q in self.ref_path_map[ref.path]:
num = i
break
if num is not None:
self.scroll_pos = min(num, self.max_scroll_pos)
@property @property
def num_lines(self): def num_lines(self):
return self.screen_size.rows - 1 return self.screen_size.rows - 1
@ -132,6 +162,15 @@ class DiffHandler(Handler):
self.cmd.clear_to_eol() self.cmd.clear_to_eol()
self.write(':') self.write(':')
def change_context_count(self, new_ctx):
new_ctx = max(0, new_ctx)
if new_ctx != self.current_context_count:
self.current_context_count = new_ctx
self.state = COLLECTED
self.generate_diff()
self.restore_position = self.current_position
self.draw_screen()
def on_text(self, text, in_bracketed_paste=False): def on_text(self, text, in_bracketed_paste=False):
if text == 'q': if text == 'q':
if self.state <= DIFFED: if self.state <= DIFFED:
@ -141,6 +180,15 @@ class DiffHandler(Handler):
if text in 'jk': if text in 'jk':
self.scroll_lines(1 if text == 'j' else -1) self.scroll_lines(1 if text == 'j' else -1)
return return
if text in 'a+-=':
new_ctx = self.current_context_count
if text == 'a':
new_ctx = 100000
elif text == '=':
new_ctx = 3
else:
new_ctx += (-1 if text == '-' else 1) * 5
self.change_context_count(new_ctx)
def on_key(self, key_event): def on_key(self, key_event):
if key_event.type is RELEASE: if key_event.type is RELEASE:
@ -176,6 +224,7 @@ class DiffHandler(Handler):
return return
if job_id == 'collect': if job_id == 'collect':
self.collection = job_result['result'] self.collection = job_result['result']
self.state = COLLECTED
self.generate_diff() self.generate_diff()
elif job_id == 'diff': elif job_id == 'diff':
diff_map = job_result['result'] diff_map = job_result['result']
@ -186,6 +235,10 @@ class DiffHandler(Handler):
self.state = DIFFED self.state = DIFFED
self.diff_map = diff_map self.diff_map = diff_map
self.render_diff() self.render_diff()
self.scroll_pos = 0
if self.restore_position is not None:
self.current_position = self.restore_position
self.restore_position = None
self.draw_screen() self.draw_screen()
if initialize_highlighter is not None and not self.highlighting_done: if initialize_highlighter is not None and not self.highlighting_done:
self.highlighting_done = True self.highlighting_done = True

View File

@ -15,32 +15,32 @@ from .config import formats
from .diff_speedup import split_with_highlights as _split_with_highlights from .diff_speedup import split_with_highlights as _split_with_highlights
class HunkRef: class Ref:
__slots__ = ('hunk_num', 'line_num', 'chunk_num') def __setattr__(self, name, value):
raise AttributeError("can't set attribute")
def __init__(self, hunk_num, chunk_num=None, line_num=None): def __repr__(self):
self.hunk_num = hunk_num return '{}({})'.format(self.__class__.__name__, ', '.join(
self.chunk_num = chunk_num '{}={}'.format(n, getattr(self, n)) for n in self.__slots__ if n != '_hash'))
self.line_num = line_num
class LineRef: class LineRef(Ref):
__slots__ = ('src_line_number', 'wrapped_line_idx') __slots__ = ('src_line_number', 'wrapped_line_idx')
def __init__(self, sln, wli): def __init__(self, sln, wli=0):
self.src_line_number = sln object.__setattr__(self, 'src_line_number', sln)
self.wrapped_line_idx = wli object.__setattr__(self, 'wrapped_line_idx', wli)
class Reference: class Reference(Ref):
__slots__ = ('path', 'extra') __slots__ = ('path', 'extra')
def __init__(self, path, extra=None): def __init__(self, path, extra=None):
self.path = path object.__setattr__(self, 'path', path)
self.extra = extra object.__setattr__(self, 'extra', extra)
class Line: class Line:
@ -243,41 +243,42 @@ def render_half_line(line_number, line, highlights, ltype, margin_size, availabl
def lines_for_chunk(data, hunk_num, chunk, chunk_num): def lines_for_chunk(data, hunk_num, chunk, chunk_num):
if chunk.is_context: if chunk.is_context:
for i in range(chunk.left_count): for i in range(chunk.left_count):
left_line_number = chunk.left_start + i left_line_number = line_ref = chunk.left_start + i
right_line_number = chunk.right_start + i right_line_number = chunk.right_start + i
highlights = data.left_highlights_for_line(left_line_number) highlights = data.left_highlights_for_line(left_line_number)
if highlights: if highlights:
lines = split_with_highlights(data.left_lines[left_line_number], data.available_cols, highlights) lines = split_with_highlights(data.left_lines[left_line_number], data.available_cols, highlights)
else: else:
lines = split_to_size(data.left_lines[left_line_number], data.available_cols) lines = split_to_size(data.left_lines[left_line_number], data.available_cols)
ref = Reference(data.left_path, HunkRef(hunk_num, chunk_num, i))
left_line_number = str(left_line_number + 1) left_line_number = str(left_line_number + 1)
right_line_number = str(right_line_number + 1) right_line_number = str(right_line_number + 1)
for text in lines: for wli, text in enumerate(lines):
line = render_diff_line(left_line_number, text, 'context', data.margin_size, data.available_cols) line = render_diff_line(left_line_number, text, 'context', data.margin_size, data.available_cols)
if right_line_number == left_line_number: if right_line_number == left_line_number:
r = line r = line
else: else:
r = render_diff_line(right_line_number, text, 'context', data.margin_size, data.available_cols) r = render_diff_line(right_line_number, text, 'context', data.margin_size, data.available_cols)
ref = Reference(data.left_path, LineRef(line_ref, wli))
yield Line(line + r, ref) yield Line(line + r, ref)
left_line_number = right_line_number = '' left_line_number = right_line_number = ''
else: else:
common = min(chunk.left_count, chunk.right_count) common = min(chunk.left_count, chunk.right_count)
for i in range(max(chunk.left_count, chunk.right_count)): for i in range(max(chunk.left_count, chunk.right_count)):
ref = Reference(data.left_path, HunkRef(hunk_num, chunk_num, i))
ll, rl = [], [] ll, rl = [], []
if i < chunk.left_count: if i < chunk.left_count:
rln = chunk.left_start + i rln = ref_ln = chunk.left_start + i
ll.extend(render_half_line( ll.extend(render_half_line(
rln, data.left_lines[rln], data.left_highlights_for_line(rln), rln, data.left_lines[rln], data.left_highlights_for_line(rln),
'remove', data.margin_size, data.available_cols, 'remove', data.margin_size, data.available_cols,
None if chunk.centers is None else chunk.centers[i])) None if chunk.centers is None else chunk.centers[i]))
ref_path = data.left_path
if i < chunk.right_count: if i < chunk.right_count:
rln = chunk.right_start + i rln = ref_ln = chunk.right_start + i
rl.extend(render_half_line( rl.extend(render_half_line(
rln, data.right_lines[rln], data.right_highlights_for_line(rln), rln, data.right_lines[rln], data.right_highlights_for_line(rln),
'add', data.margin_size, data.available_cols, 'add', data.margin_size, data.available_cols,
None if chunk.centers is None else chunk.centers[i])) None if chunk.centers is None else chunk.centers[i]))
ref_path = data.right_path
if i < common: if i < common:
extra = len(ll) - len(rl) extra = len(ll) - len(rl)
if extra != 0: if extra != 0:
@ -293,7 +294,8 @@ def lines_for_chunk(data, hunk_num, chunk, chunk_num):
else: else:
x, count = ll, len(rl) x, count = ll, len(rl)
x.extend(repeat(data.filler_line, count)) x.extend(repeat(data.filler_line, count))
for left_line, right_line in zip(ll, rl): for wli, (left_line, right_line) in enumerate(zip(ll, rl)):
ref = Reference(ref_path, LineRef(ref_ln, wli))
yield Line(left_line + right_line, ref) yield Line(left_line + right_line, ref)
@ -302,7 +304,7 @@ def lines_for_diff(left_path, right_path, hunks, args, columns, margin_size):
data = DiffData(left_path, right_path, available_cols, margin_size) data = DiffData(left_path, right_path, available_cols, margin_size)
for hunk_num, hunk in enumerate(hunks): for hunk_num, hunk in enumerate(hunks):
yield Line(hunk_title(hunk_num, hunk, margin_size, columns - margin_size), Reference(left_path, HunkRef(hunk_num))) yield Line(hunk_title(hunk_num, hunk, margin_size, columns - margin_size), Reference(left_path, LineRef(hunk.left_start)))
for cnum, chunk in enumerate(hunk.chunks): for cnum, chunk in enumerate(hunk.chunks):
yield from lines_for_chunk(data, hunk_num, chunk, cnum) yield from lines_for_chunk(data, hunk_num, chunk, cnum)