diff kitten: keyboard shortcuts to change the number of lines of context
This commit is contained in:
parent
96793a296c
commit
324c223d54
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user