1#!/usr/local/bin/python3.8
2# vim:fileencoding=utf-8
3# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
4
5
6from typing import TYPE_CHECKING, Optional, Tuple, Union
7
8from .base import (
9    MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType,
10    PayloadType, RCOptions, RemoteCommand, ResponseType, Window
11)
12
13if TYPE_CHECKING:
14    from kitty.cli_stub import ScrollWindowRCOptions as CLIOptions
15
16
17class ScrollWindow(RemoteCommand):
18
19    '''
20    amount+: The amount to scroll, a two item list with the first item being \
21             either a number or the keywords, start and end. \
22             And the second item being either 'p' for pages or 'l' for lines or 'u'
23             for unscrolling by lines.
24    match: The window to scroll
25    '''
26
27    short_desc = 'Scroll the specified window'
28    desc = (
29        'Scroll the specified window, if no window is specified, scroll the window this command is run inside.'
30        ' SCROLL_AMOUNT can be either the keywords :code:`start` or :code:`end` or an'
31        ' argument of the form <number>[unit][+-]. For example, 30 will scroll down 30 lines and 2p- will'
32        ' scroll up 2 pages. 3u will *unscroll* by 3 lines, which means that 3 lines will move from the'
33        ' scrollback buffer onto the top of the screen.'
34    )
35    argspec = 'SCROLL_AMOUNT'
36    options_spec = MATCH_WINDOW_OPTION
37
38    def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
39        amt = args[0]
40        amount: Tuple[Union[str, int], Optional[str]] = (amt, None)
41        if amt not in ('start', 'end'):
42            pages = 'p' in amt
43            unscroll = 'u' in amt
44            amt = amt.replace('p', '')
45            mult = -1 if amt.endswith('-') and not unscroll else 1
46            q = int(amt.replace('-', ''))
47            amount = q * mult, 'p' if pages else ('u' if unscroll else 'l')
48
49        return {'match': opts.match, 'amount': amount}
50
51    def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType:
52        windows = [window or boss.active_window]
53        match = payload_get('match')
54        amt = payload_get('amount')
55        if match:
56            windows = list(boss.match_windows(match))
57            if not windows:
58                raise MatchError(match)
59        for window in windows:
60            if window:
61                if amt[0] in ('start', 'end'):
62                    getattr(window, {'start': 'scroll_home'}.get(amt[0], 'scroll_end'))()
63                else:
64                    amt, unit = amt
65                    if unit == 'u':
66                        window.screen.reverse_scroll(abs(amt), True)
67                    else:
68                        unit = 'page' if unit == 'p' else 'line'
69                        direction = 'up' if amt < 0 else 'down'
70                        func = getattr(window, 'scroll_{}_{}'.format(unit, direction))
71                        for i in range(abs(amt)):
72                            func()
73
74
75scroll_window = ScrollWindow()
76