mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-13 20:14:12 +02:00
more typing work
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user