more typing work

This commit is contained in:
Kovid Goyal
2020-03-11 20:08:16 +05:30
parent 0e871a89aa
commit 2ebdf738ca
10 changed files with 320 additions and 244 deletions

View File

@@ -13,22 +13,27 @@ from collections import defaultdict
from contextlib import suppress
from functools import partial
from gettext import gettext as _
from typing import DefaultDict, List, Tuple
from typing import DefaultDict, Dict, Iterable, List, Optional, Tuple, Union
from kitty.cli import CONFIG_HELP, parse_args
from kitty.cli_stub import DiffCLIOptions
from kitty.conf.utils import KittensKeyAction
from kitty.constants import appname
from kitty.fast_data_types import wcswidth
from kitty.key_encoding import RELEASE, enter_key, key_defs as K
from kitty.key_encoding import RELEASE, KeyEvent, enter_key, key_defs as K
from kitty.options_stub import DiffOptions
from kitty.utils import ScreenSize
from . import global_data
from .collect import (
create_collection, data_for_path, lines_for_path, sanitize,
Collection, create_collection, data_for_path, lines_for_path, sanitize,
set_highlight_data
)
from . import global_data
from .config import init_config
from .patch import Differ, set_diff_command, worker_processes
from .render import ImageSupportWarning, LineRef, Reference, render_diff
from .patch import Differ, Patch, set_diff_command, worker_processes
from .render import (
ImagePlacement, ImageSupportWarning, Line, LineRef, Reference, render_diff
)
from .search import BadRegex, Search
from ..tui.handler import Handler
from ..tui.images import ImageManager
@@ -37,8 +42,9 @@ from ..tui.loop import Loop
from ..tui.operations import styled
try:
from .highlight import initialize_highlighter, highlight_collection
from .highlight import initialize_highlighter, highlight_collection, DiffHighlight
has_highlighter = True
DiffHighlight
except ImportError:
has_highlighter = False
@@ -47,13 +53,14 @@ INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
ESCAPE = K['ESCAPE']
def generate_diff(collection, context):
def generate_diff(collection: Collection, context: int) -> Union[str, Dict[str, Patch]]:
d = Differ()
for path, item_type, changed_path in collection:
if item_type == 'diff':
is_binary = isinstance(data_for_path(path), bytes) or isinstance(data_for_path(changed_path), bytes)
if not is_binary:
assert changed_path is not None
d.add_diff(path, changed_path)
return d(context)
@@ -63,35 +70,35 @@ class DiffHandler(Handler):
image_manager_class = ImageManager
def __init__(self, args, opts, left, right):
def __init__(self, args: DiffCLIOptions, opts: DiffOptions, left: str, right: str) -> None:
self.state = INITIALIZING
self.message = ''
self.current_search_is_regex = True
self.current_search = None
self.current_search: Optional[Search] = None
self.line_edit = LineEdit()
self.opts = opts
self.left, self.right = left, right
self.report_traceback_on_exit = None
self.report_traceback_on_exit: Union[str, Dict[str, Patch], None] = None
self.args = args
self.scroll_pos = self.max_scroll_pos = 0
self.current_context_count = self.original_context_count = self.args.context
if self.current_context_count < 0:
self.current_context_count = self.original_context_count = self.opts.num_context_lines
self.highlighting_done = False
self.restore_position = None
self.restore_position: Optional[Reference] = None
for key_def, action in self.opts.key_definitions.items():
self.add_shortcut(action, *key_def)
def perform_action(self, action):
def perform_action(self, action: KittensKeyAction) -> None:
func, args = action
if func == 'quit':
self.quit_loop(0)
return
if self.state <= DIFFED:
if func == 'scroll_by':
return self.scroll_lines(*args)
return self.scroll_lines(int(args[0]))
if func == 'scroll_to':
where = args[0]
where = str(args[0])
if 'change' in where:
return self.scroll_to_next_change(backwards='prev' in where)
if 'match' in where:
@@ -103,7 +110,7 @@ class DiffHandler(Handler):
return self.scroll_lines(amt)
if func == 'change_context':
new_ctx = self.current_context_count
to = args[0]
to = int(args[0])
if to == 'all':
new_ctx = 100000
elif to == 'default':
@@ -112,25 +119,25 @@ class DiffHandler(Handler):
new_ctx += to
return self.change_context_count(new_ctx)
if func == 'start_search':
self.start_search(*args)
self.start_search(bool(args[0]), bool(args[1]))
return
def create_collection(self):
def create_collection(self) -> None:
def collect_done(collection):
def collect_done(collection: Collection) -> None:
self.collection = collection
self.state = COLLECTED
self.generate_diff()
def collect(left, right):
def collect(left: str, right: str) -> None:
collection = create_collection(left, right)
self.asyncio_loop.call_soon_threadsafe(collect_done, collection)
self.asyncio_loop.run_in_executor(None, collect, self.left, self.right)
def generate_diff(self):
def generate_diff(self) -> None:
def diff_done(diff_map):
def diff_done(diff_map: Union[str, Dict[str, Patch]]) -> None:
if isinstance(diff_map, str):
self.report_traceback_on_exit = diff_map
self.quit_loop(1)
@@ -155,15 +162,15 @@ class DiffHandler(Handler):
return
self.syntax_highlight()
def diff(collection, current_context_count):
def diff(collection: Collection, current_context_count: int) -> None:
diff_map = generate_diff(collection, current_context_count)
self.asyncio_loop.call_soon_threadsafe(diff_done, diff_map)
self.asyncio_loop.run_in_executor(None, diff, self.collection, self.current_context_count)
def syntax_highlight(self):
def syntax_highlight(self) -> None:
def highlighting_done(hdata):
def highlighting_done(hdata: Union[str, Dict[str, 'DiffHighlight']]) -> None:
if isinstance(hdata, str):
self.report_traceback_on_exit = hdata
self.quit_loop(1)
@@ -172,21 +179,21 @@ class DiffHandler(Handler):
self.render_diff()
self.draw_screen()
def highlight(*a):
result = highlight_collection(*a)
def highlight(collection: Collection, aliases: Optional[Dict[str, str]] = None) -> None:
result = highlight_collection(collection, aliases)
self.asyncio_loop.call_soon_threadsafe(highlighting_done, result)
self.asyncio_loop.run_in_executor(None, highlight, self.collection, self.opts.syntax_aliases)
def calculate_statistics(self):
def calculate_statistics(self) -> None:
self.added_count = self.collection.added_count
self.removed_count = self.collection.removed_count
for patch in self.diff_map.values():
self.added_count += patch.added_count
self.removed_count += patch.removed_count
def render_diff(self):
self.diff_lines = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols, self.image_manager))
def render_diff(self) -> None:
self.diff_lines: Tuple[Line, ...] = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols, self.image_manager))
self.margin_size = render_diff.margin_size
self.ref_path_map: DefaultDict[str, List[Tuple[int, Reference]]] = defaultdict(list)
for i, l in enumerate(self.diff_lines):
@@ -196,11 +203,11 @@ class DiffHandler(Handler):
self.current_search(self.diff_lines, self.margin_size, self.screen_size.cols)
@property
def current_position(self):
def current_position(self) -> Reference:
return self.diff_lines[min(len(self.diff_lines) - 1, self.scroll_pos)].ref
@current_position.setter
def current_position(self, ref):
def current_position(self, ref: Reference) -> None:
num = None
if isinstance(ref.extra, LineRef):
sln = ref.extra.src_line_number
@@ -220,10 +227,10 @@ class DiffHandler(Handler):
self.scroll_pos = max(0, min(num, self.max_scroll_pos))
@property
def num_lines(self):
def num_lines(self) -> int:
return self.screen_size.rows - 1
def scroll_to_next_change(self, backwards=False):
def scroll_to_next_change(self, backwards: bool = False) -> None:
if backwards:
r = range(self.scroll_pos - 1, -1, -1)
else:
@@ -235,7 +242,7 @@ class DiffHandler(Handler):
return
self.cmd.bell()
def scroll_to_next_match(self, backwards=False, include_current=False):
def scroll_to_next_match(self, backwards: bool = False, include_current: bool = False) -> None:
if self.current_search is not None:
offset = 0 if include_current else 1
if backwards:
@@ -248,10 +255,10 @@ class DiffHandler(Handler):
return
self.cmd.bell()
def set_scrolling_region(self):
def set_scrolling_region(self) -> None:
self.cmd.set_scrolling_region(self.screen_size, 0, self.num_lines - 2)
def scroll_lines(self, amt=1):
def scroll_lines(self, amt: int = 1) -> None:
new_pos = max(0, min(self.scroll_pos + amt, self.max_scroll_pos))
if new_pos == self.scroll_pos:
self.cmd.bell()
@@ -271,7 +278,7 @@ class DiffHandler(Handler):
self.draw_lines(amt, self.num_lines - amt)
self.draw_status_line()
def init_terminal_state(self):
def init_terminal_state(self) -> None:
self.cmd.set_line_wrapping(False)
self.cmd.set_window_title(global_data.title)
self.cmd.set_default_colors(
@@ -280,21 +287,21 @@ class DiffHandler(Handler):
select_bg=self.opts.select_bg)
self.cmd.set_cursor_shape('bar')
def finalize(self):
def finalize(self) -> None:
self.cmd.set_default_colors()
self.cmd.set_cursor_visible(True)
self.cmd.set_scrolling_region()
def initialize(self):
def initialize(self) -> None:
self.init_terminal_state()
self.set_scrolling_region()
self.draw_screen()
self.create_collection()
def enforce_cursor_state(self):
def enforce_cursor_state(self) -> None:
self.cmd.set_cursor_visible(self.state == COMMAND)
def draw_lines(self, num, offset=0):
def draw_lines(self, num: int, offset: int = 0) -> None:
offset += self.scroll_pos
image_involved = False
limit = len(self.diff_lines)
@@ -315,7 +322,7 @@ class DiffHandler(Handler):
if image_involved:
self.place_images()
def place_images(self):
def place_images(self) -> None:
self.cmd.clear_images_on_screen()
offset = self.scroll_pos
limit = len(self.diff_lines)
@@ -338,7 +345,7 @@ class DiffHandler(Handler):
self.place_image(row, right_placement, False)
in_image = True
def place_image(self, row, placement, is_left):
def place_image(self, row: int, placement: ImagePlacement, is_left: bool) -> None:
xpos = (0 if is_left else (self.screen_size.cols // 2)) + placement.image.margin_size
image_height_in_rows = placement.image.rows
topmost_visible_row = placement.row
@@ -350,7 +357,7 @@ class DiffHandler(Handler):
self.image_manager.show_image(placement.image.image_id, xpos, row, src_rect=(
0, top, placement.image.width, height))
def draw_screen(self):
def draw_screen(self) -> None:
self.enforce_cursor_state()
if self.state < DIFFED:
self.cmd.clear_screen()
@@ -361,7 +368,7 @@ class DiffHandler(Handler):
self.draw_lines(self.num_lines)
self.draw_status_line()
def draw_status_line(self):
def draw_status_line(self) -> None:
if self.state < DIFFED:
return
self.enforce_cursor_state()
@@ -388,7 +395,7 @@ class DiffHandler(Handler):
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)
self.write(text)
def change_context_count(self, new_ctx):
def change_context_count(self, new_ctx: int) -> None:
new_ctx = max(0, new_ctx)
if new_ctx != self.current_context_count:
self.current_context_count = new_ctx
@@ -397,7 +404,7 @@ class DiffHandler(Handler):
self.restore_position = self.current_position
self.draw_screen()
def start_search(self, is_regex, is_backward):
def start_search(self, is_regex: bool, is_backward: bool) -> None:
if self.state != DIFFED:
self.cmd.bell()
return
@@ -407,7 +414,7 @@ class DiffHandler(Handler):
self.current_search_is_regex = is_regex
self.draw_status_line()
def do_search(self):
def do_search(self) -> None:
self.current_search = None
query = self.line_edit.current_input
if len(query) < 2:
@@ -426,7 +433,7 @@ class DiffHandler(Handler):
self.message = sanitize(_('No matches found'))
self.cmd.bell()
def on_text(self, text, in_bracketed_paste=False):
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
if self.state is COMMAND:
self.line_edit.on_text(text, in_bracketed_paste)
self.draw_status_line()
@@ -439,7 +446,7 @@ class DiffHandler(Handler):
if action is not None:
return self.perform_action(action)
def on_key(self, key_event):
def on_key(self, key_event: KeyEvent) -> None:
if self.state is MESSAGE:
if key_event.type is not RELEASE:
self.state = DIFFED
@@ -472,7 +479,7 @@ class DiffHandler(Handler):
if action is not None:
return self.perform_action(action)
def on_resize(self, screen_size):
def on_resize(self, screen_size: ScreenSize) -> None:
self.screen_size = screen_size
self.set_scrolling_region()
if self.state > COLLECTED:
@@ -480,10 +487,10 @@ class DiffHandler(Handler):
self.render_diff()
self.draw_screen()
def on_interrupt(self):
def on_interrupt(self) -> None:
self.quit_loop(1)
def on_eot(self):
def on_eot(self) -> None:
self.quit_loop(1)
@@ -510,10 +517,10 @@ Syntax: :italic:`name=value`. For example: :italic:`-o background=gray`
class ShowWarning:
def __init__(self):
self.warnings = []
def __init__(self) -> None:
self.warnings: List[str] = []
def __call__(self, message, category, filename, lineno, file=None, line=None):
def __call__(self, message: str, category: object, filename: str, lineno: int, file: object = None, line: object = None) -> None:
if category is ImageSupportWarning:
showwarning.warnings.append(message)
@@ -523,13 +530,13 @@ help_text = 'Show a side-by-side diff of the specified files/directories. You ca
usage = 'file_or_directory_left file_or_directory_right'
def terminate_processes(processes):
def terminate_processes(processes: Iterable[int]) -> None:
for pid in processes:
with suppress(Exception):
os.kill(pid, signal.SIGKILL)
def get_remote_file(path):
def get_remote_file(path: str) -> str:
if path.startswith('ssh:'):
parts = path.split(':', 2)
if len(parts) == 3:
@@ -543,16 +550,16 @@ def get_remote_file(path):
return path
def main(args):
def main(args: List[str]) -> None:
warnings.showwarning = showwarning
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff', result_class=DiffCLIOptions)
cli_opts, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff', result_class=DiffCLIOptions)
if len(items) != 2:
raise SystemExit('You must specify exactly two files/directories to compare')
left, right = items
global_data.title = _('{} vs. {}').format(left, right)
if os.path.isdir(left) != os.path.isdir(right):
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
opts = init_config(args)
opts = init_config(cli_opts)
set_diff_command(opts.diff_cmd)
lines_for_path.replace_tab_by = opts.replace_tab_by
left, right = map(get_remote_file, (left, right))
@@ -561,7 +568,7 @@ def main(args):
raise SystemExit('{} does not exist'.format(f))
loop = Loop()
handler = DiffHandler(args, opts, left, right)
handler = DiffHandler(cli_opts, opts, left, right)
loop.loop(handler)
for message in showwarning.warnings:
from kitty.utils import safe_print