parent
c994bc1d89
commit
fe3b10a8fb
@ -67,21 +67,25 @@ Keyboard controls
|
|||||||
========================= ===========================
|
========================= ===========================
|
||||||
Action Shortcut
|
Action Shortcut
|
||||||
========================= ===========================
|
========================= ===========================
|
||||||
Quit ``q, Ctrl+c``
|
Quit :kbd:`q, Ctrl+c`
|
||||||
Scroll line up ``k, up``
|
Scroll line up :kbd:`k, up`
|
||||||
Scroll line down ``j, down``
|
Scroll line down :kbd:`j, down`
|
||||||
Scroll page up ``PgUp``
|
Scroll page up :kbd:`PgUp`
|
||||||
Scroll page down ``PgDn``
|
Scroll page down :kbd:`PgDn`
|
||||||
Scroll to top ``Home``
|
Scroll to top :kbd:`Home`
|
||||||
Scroll to bottom ``End``
|
Scroll to bottom :kbd:`End`
|
||||||
Scroll to next page ``Space, PgDn``
|
Scroll to next page :kbd:`Space, PgDn`
|
||||||
Scroll to previous page ``PgUp``
|
Scroll to previous page :kbd:`PgUp`
|
||||||
Scroll to next change ``n``
|
Scroll to next change :kbd:`n`
|
||||||
Scroll to previous change ``p``
|
Scroll to previous change :kbd:`p`
|
||||||
Increase lines of context ``+``
|
Increase lines of context :kbd:`+`
|
||||||
Decrease lines of context ``-``
|
Decrease lines of context :kbd:`-`
|
||||||
All lines of context ``a``
|
All lines of context :kbd:`a`
|
||||||
Restore default context ``=``
|
Restore default context :kbd:`=`
|
||||||
|
Search forwards :kbd:`/`
|
||||||
|
Search backwards :kbd:`?`
|
||||||
|
Scroll to next match :kbd:`>, .`
|
||||||
|
Scroll to previous match :kbd:`<, ,`
|
||||||
========================= ===========================
|
========================= ===========================
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ def parse_scroll_by(func, rest):
|
|||||||
@func_with_args('scroll_to')
|
@func_with_args('scroll_to')
|
||||||
def parse_scroll_to(func, rest):
|
def parse_scroll_to(func, rest):
|
||||||
rest = rest.lower()
|
rest = rest.lower()
|
||||||
if rest not in {'start', 'end', 'next-change', 'prev-change', 'next-page', 'prev-page'}:
|
if rest not in {'start', 'end', 'next-change', 'prev-change', 'next-page', 'prev-page', 'next-match', 'prev-match'}:
|
||||||
rest = 'start'
|
rest = 'start'
|
||||||
return func, rest
|
return func, rest
|
||||||
|
|
||||||
@ -69,6 +69,14 @@ def parse_change_context(func, rest):
|
|||||||
return func, amount
|
return func, amount
|
||||||
|
|
||||||
|
|
||||||
|
@func_with_args('start_search')
|
||||||
|
def parse_start_search(func, rest):
|
||||||
|
rest = rest.lower().split()
|
||||||
|
is_regex = rest and rest[0] == 'regex'
|
||||||
|
is_backward = len(rest) > 1 and rest[1] == 'backward'
|
||||||
|
return func, (is_regex, is_backward)
|
||||||
|
|
||||||
|
|
||||||
def special_handling(key, val, ans):
|
def special_handling(key, val, ans):
|
||||||
if key == 'map':
|
if key == 'map':
|
||||||
action, *key_def = parse_kittens_key(val, args_funcs)
|
action, *key_def = parse_kittens_key(val, args_funcs)
|
||||||
|
|||||||
@ -83,6 +83,8 @@ c('filler_bg', '#fafbfc', long_text=_('Filler (empty) line background'))
|
|||||||
c('hunk_margin_bg', '#dbedff', long_text=_('Hunk header colors'))
|
c('hunk_margin_bg', '#dbedff', long_text=_('Hunk header colors'))
|
||||||
c('hunk_bg', '#f1f8ff')
|
c('hunk_bg', '#f1f8ff')
|
||||||
|
|
||||||
|
c('search_bg', '#444', long_text=_('Search highlighting'))
|
||||||
|
c('search_fg', 'white')
|
||||||
|
|
||||||
g('shortcuts')
|
g('shortcuts')
|
||||||
k('quit', 'q', 'quit', _('Quit'))
|
k('quit', 'q', 'quit', _('Quit'))
|
||||||
@ -108,4 +110,11 @@ k('default_context', '=', 'change_context default', _('Show default context'))
|
|||||||
k('increase_context', '+', 'change_context 5', _('Increase context'))
|
k('increase_context', '+', 'change_context 5', _('Increase context'))
|
||||||
k('decrease_context', '-', 'change_context -5', _('Decrease context'))
|
k('decrease_context', '-', 'change_context -5', _('Decrease context'))
|
||||||
|
|
||||||
|
k('search_forward', '/', 'start_search regex forward', _('Search forward'))
|
||||||
|
k('search_backward', '?', 'start_search regex backward', _('Search backward'))
|
||||||
|
k('next_match', '.', 'scroll_to next-match', _('Scroll to next search match'))
|
||||||
|
k('prev_match', ',', 'scroll_to prev-match', _('Scroll to previous search match'))
|
||||||
|
k('next_match', '>', 'scroll_to next-match', _('Scroll to next search match'))
|
||||||
|
k('prev_match', '<', 'scroll_to prev-match', _('Scroll to previous search match'))
|
||||||
|
|
||||||
type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')}
|
type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')}
|
||||||
|
|||||||
@ -11,18 +11,21 @@ from gettext import gettext as _
|
|||||||
|
|
||||||
from kitty.cli import CONFIG_HELP, appname, parse_args
|
from kitty.cli import CONFIG_HELP, appname, parse_args
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import RELEASE
|
from kitty.key_encoding import ESCAPE, RELEASE, enter_key
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
from ..tui.images import ImageManager
|
from ..tui.images import ImageManager
|
||||||
|
from ..tui.line_edit import LineEdit
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
from ..tui.operations import styled
|
from ..tui.operations import styled
|
||||||
from .collect import (
|
from .collect import (
|
||||||
create_collection, data_for_path, lines_for_path, set_highlight_data
|
create_collection, data_for_path, lines_for_path, sanitize,
|
||||||
|
set_highlight_data
|
||||||
)
|
)
|
||||||
from .config import init_config
|
from .config import init_config
|
||||||
from .patch import Differ, set_diff_command
|
from .patch import Differ, set_diff_command
|
||||||
from .render import ImageSupportWarning, LineRef, render_diff
|
from .render import ImageSupportWarning, LineRef, render_diff
|
||||||
|
from .search import BadRegex, Search
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .highlight import initialize_highlighter, highlight_collection
|
from .highlight import initialize_highlighter, highlight_collection
|
||||||
@ -30,7 +33,7 @@ except ImportError:
|
|||||||
initialize_highlighter = None
|
initialize_highlighter = None
|
||||||
|
|
||||||
|
|
||||||
INITIALIZING, COLLECTED, DIFFED = range(3)
|
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
||||||
|
|
||||||
|
|
||||||
def generate_diff(collection, context):
|
def generate_diff(collection, context):
|
||||||
@ -51,6 +54,10 @@ class DiffHandler(Handler):
|
|||||||
|
|
||||||
def __init__(self, args, opts, left, right):
|
def __init__(self, args, opts, left, right):
|
||||||
self.state = INITIALIZING
|
self.state = INITIALIZING
|
||||||
|
self.message = ''
|
||||||
|
self.current_search_is_regex = True
|
||||||
|
self.current_search = None
|
||||||
|
self.line_edit = LineEdit()
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.left, self.right = left, right
|
self.left, self.right = left, right
|
||||||
self.report_traceback_on_exit = None
|
self.report_traceback_on_exit = None
|
||||||
@ -76,6 +83,8 @@ class DiffHandler(Handler):
|
|||||||
where = args[0]
|
where = args[0]
|
||||||
if 'change' in where:
|
if 'change' in where:
|
||||||
return self.scroll_to_next_change(backwards='prev' in where)
|
return self.scroll_to_next_change(backwards='prev' in where)
|
||||||
|
if 'match' in where:
|
||||||
|
return self.scroll_to_next_match(backwards='prev' in where)
|
||||||
if 'page' in where:
|
if 'page' in where:
|
||||||
amt = self.num_lines * (1 if 'next' in where else -1)
|
amt = self.num_lines * (1 if 'next' in where else -1)
|
||||||
else:
|
else:
|
||||||
@ -91,6 +100,9 @@ class DiffHandler(Handler):
|
|||||||
else:
|
else:
|
||||||
new_ctx += to
|
new_ctx += to
|
||||||
return self.change_context_count(new_ctx)
|
return self.change_context_count(new_ctx)
|
||||||
|
if func == 'start_search':
|
||||||
|
self.start_search(*args)
|
||||||
|
return
|
||||||
|
|
||||||
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)
|
||||||
@ -107,10 +119,13 @@ class DiffHandler(Handler):
|
|||||||
|
|
||||||
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.image_manager))
|
self.diff_lines = 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(list)
|
self.ref_path_map = defaultdict(list)
|
||||||
for i, l in enumerate(self.diff_lines):
|
for i, l in enumerate(self.diff_lines):
|
||||||
self.ref_path_map[l.ref.path].append((i, l.ref))
|
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
|
||||||
|
if self.current_search is not None:
|
||||||
|
self.current_search(self.diff_lines, self.margin_size, self.screen_size.cols)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_position(self):
|
def current_position(self):
|
||||||
@ -152,6 +167,18 @@ class DiffHandler(Handler):
|
|||||||
return
|
return
|
||||||
self.cmd.bell()
|
self.cmd.bell()
|
||||||
|
|
||||||
|
def scroll_to_next_match(self, backwards=False):
|
||||||
|
if self.current_search is not None:
|
||||||
|
if backwards:
|
||||||
|
r = range(self.scroll_pos - 1, -1, -1)
|
||||||
|
else:
|
||||||
|
r = range(self.scroll_pos + 1, len(self.diff_lines))
|
||||||
|
for i in r:
|
||||||
|
if i in self.current_search:
|
||||||
|
self.scroll_lines(i - self.scroll_pos)
|
||||||
|
return
|
||||||
|
self.cmd.bell()
|
||||||
|
|
||||||
def set_scrolling_region(self):
|
def set_scrolling_region(self):
|
||||||
self.cmd.set_scrolling_region(self.screen_size, 0, self.num_lines - 2)
|
self.cmd.set_scrolling_region(self.screen_size, 0, self.num_lines - 2)
|
||||||
|
|
||||||
@ -178,7 +205,8 @@ class DiffHandler(Handler):
|
|||||||
def init_terminal_state(self):
|
def init_terminal_state(self):
|
||||||
self.cmd.set_line_wrapping(False)
|
self.cmd.set_line_wrapping(False)
|
||||||
self.cmd.set_window_title(main.title)
|
self.cmd.set_window_title(main.title)
|
||||||
self.cmd.set_default_colors(self.opts.foreground, self.opts.background)
|
self.cmd.set_default_colors(self.opts.foreground, self.opts.background, self.opts.foreground)
|
||||||
|
self.cmd.set_cursor_shape('bar')
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.init_terminal_state()
|
self.init_terminal_state()
|
||||||
@ -191,7 +219,6 @@ class DiffHandler(Handler):
|
|||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
self.cmd.set_cursor_visible(True)
|
self.cmd.set_cursor_visible(True)
|
||||||
self.cmd.set_default_colors()
|
|
||||||
self.cmd.set_scrolling_region()
|
self.cmd.set_scrolling_region()
|
||||||
|
|
||||||
def draw_lines(self, num, offset=0):
|
def draw_lines(self, num, offset=0):
|
||||||
@ -208,6 +235,8 @@ class DiffHandler(Handler):
|
|||||||
if line.image_data is not None:
|
if line.image_data is not None:
|
||||||
image_involved = True
|
image_involved = True
|
||||||
self.write('\r\x1b[K' + text + '\x1b[0m')
|
self.write('\r\x1b[K' + text + '\x1b[0m')
|
||||||
|
if self.current_search is not None:
|
||||||
|
self.current_search.highlight_line(self.write, lpos)
|
||||||
if i < num - 1:
|
if i < num - 1:
|
||||||
self.write('\n')
|
self.write('\n')
|
||||||
if image_involved:
|
if image_involved:
|
||||||
@ -262,19 +291,25 @@ class DiffHandler(Handler):
|
|||||||
def draw_status_line(self):
|
def draw_status_line(self):
|
||||||
if self.state < DIFFED:
|
if self.state < DIFFED:
|
||||||
return
|
return
|
||||||
|
self.enforce_cursor_state()
|
||||||
self.cmd.set_cursor_position(0, self.num_lines)
|
self.cmd.set_cursor_position(0, self.num_lines)
|
||||||
self.cmd.clear_to_eol()
|
self.cmd.clear_to_eol()
|
||||||
scroll_frac = styled('{:.0%}'.format(self.scroll_pos / (self.max_scroll_pos or 1)), fg=self.opts.margin_fg)
|
if self.state is COMMAND:
|
||||||
counts = '{}{}{}'.format(
|
self.line_edit.write(self.write)
|
||||||
styled(str(self.added_count), fg=self.opts.highlight_added_bg),
|
elif self.state is MESSAGE:
|
||||||
styled(',', fg=self.opts.margin_fg),
|
self.write(self.message)
|
||||||
styled(str(self.removed_count), fg=self.opts.highlight_removed_bg)
|
else:
|
||||||
)
|
scroll_frac = styled('{:.0%}'.format(self.scroll_pos / (self.max_scroll_pos or 1)), fg=self.opts.margin_fg)
|
||||||
suffix = counts + ' ' + scroll_frac
|
counts = '{}{}{}'.format(
|
||||||
prefix = styled(':', fg=self.opts.margin_fg)
|
styled(str(self.added_count), fg=self.opts.highlight_added_bg),
|
||||||
filler = self.screen_size.cols - wcswidth(prefix) - wcswidth(suffix)
|
styled(',', fg=self.opts.margin_fg),
|
||||||
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)
|
styled(str(self.removed_count), fg=self.opts.highlight_removed_bg)
|
||||||
self.write(text)
|
)
|
||||||
|
suffix = counts + ' ' + scroll_frac
|
||||||
|
prefix = styled(':', fg=self.opts.margin_fg)
|
||||||
|
filler = self.screen_size.cols - wcswidth(prefix) - wcswidth(suffix)
|
||||||
|
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)
|
||||||
|
self.write(text)
|
||||||
|
|
||||||
def change_context_count(self, new_ctx):
|
def change_context_count(self, new_ctx):
|
||||||
new_ctx = max(0, new_ctx)
|
new_ctx = max(0, new_ctx)
|
||||||
@ -285,14 +320,69 @@ class DiffHandler(Handler):
|
|||||||
self.restore_position = self.current_position
|
self.restore_position = self.current_position
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
|
|
||||||
|
def start_search(self, is_regex, is_backward):
|
||||||
|
if self.state != DIFFED:
|
||||||
|
self.cmd.bell()
|
||||||
|
return
|
||||||
|
self.state = COMMAND
|
||||||
|
self.line_edit.clear()
|
||||||
|
self.line_edit.add_text('?' if is_backward else '/')
|
||||||
|
self.current_search_is_regex = is_regex
|
||||||
|
self.draw_status_line()
|
||||||
|
|
||||||
|
def do_search(self):
|
||||||
|
self.current_search = None
|
||||||
|
query = self.line_edit.current_input
|
||||||
|
if len(query) < 2:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.current_search = Search(self.opts, query[1:], self.current_search_is_regex, query[0] == '?')
|
||||||
|
except BadRegex:
|
||||||
|
self.state = MESSAGE
|
||||||
|
self.message = sanitize(_('Bad regex: {}').format(query))
|
||||||
|
self.cmd.bell()
|
||||||
|
else:
|
||||||
|
if not self.current_search(self.diff_lines, self.margin_size, self.screen_size.cols):
|
||||||
|
self.state = MESSAGE
|
||||||
|
self.message = sanitize(_('No matches found'))
|
||||||
|
self.cmd.bell()
|
||||||
|
|
||||||
def on_text(self, text, in_bracketed_paste=False):
|
def on_text(self, text, in_bracketed_paste=False):
|
||||||
|
if self.state is COMMAND:
|
||||||
|
self.line_edit.on_text(text, in_bracketed_paste)
|
||||||
|
self.draw_status_line()
|
||||||
|
return
|
||||||
|
if self.state is MESSAGE:
|
||||||
|
self.state = DIFFED
|
||||||
|
return
|
||||||
action = self.shortcut_action(text)
|
action = self.shortcut_action(text)
|
||||||
if action is not None:
|
if action is not None:
|
||||||
return self.perform_action(action)
|
return self.perform_action(action)
|
||||||
|
|
||||||
def on_key(self, key_event):
|
def on_key(self, key_event):
|
||||||
|
if self.state is MESSAGE:
|
||||||
|
if key_event.type is not RELEASE:
|
||||||
|
self.state = DIFFED
|
||||||
|
return
|
||||||
|
if self.state is COMMAND:
|
||||||
|
if self.line_edit.on_key(key_event):
|
||||||
|
if not self.line_edit.current_input:
|
||||||
|
self.state = DIFFED
|
||||||
|
self.draw_status_line()
|
||||||
|
return
|
||||||
if key_event.type is RELEASE:
|
if key_event.type is RELEASE:
|
||||||
return
|
return
|
||||||
|
if self.state is COMMAND:
|
||||||
|
if key_event.key is ESCAPE:
|
||||||
|
self.state = DIFFED
|
||||||
|
self.draw_status_line()
|
||||||
|
return
|
||||||
|
if key_event is enter_key:
|
||||||
|
self.state = DIFFED
|
||||||
|
self.do_search()
|
||||||
|
self.line_edit.clear()
|
||||||
|
self.draw_screen()
|
||||||
|
return
|
||||||
action = self.shortcut_action(key_event)
|
action = self.shortcut_action(key_event)
|
||||||
if action is not None:
|
if action is not None:
|
||||||
return self.perform_action(action)
|
return self.perform_action(action)
|
||||||
|
|||||||
@ -447,7 +447,7 @@ def render_diff(collection, diff_map, args, columns, image_manager):
|
|||||||
if patch is not None:
|
if patch is not None:
|
||||||
largest_line_number = max(largest_line_number, patch.largest_line_number)
|
largest_line_number = max(largest_line_number, patch.largest_line_number)
|
||||||
|
|
||||||
margin_size = max(3, len(str(largest_line_number)) + 1)
|
margin_size = render_diff.margin_size = max(3, len(str(largest_line_number)) + 1)
|
||||||
last_item_num = len(collection) - 1
|
last_item_num = len(collection) - 1
|
||||||
|
|
||||||
for i, (path, item_type, other_path) in enumerate(collection):
|
for i, (path, item_type, other_path) in enumerate(collection):
|
||||||
|
|||||||
64
kittens/diff/search.py
Normal file
64
kittens/diff/search.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from kitty.fast_data_types import wcswidth
|
||||||
|
|
||||||
|
from ..tui.operations import styled
|
||||||
|
|
||||||
|
|
||||||
|
class BadRegex(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Search:
|
||||||
|
|
||||||
|
def __init__(self, opts, query, is_regex, is_backward):
|
||||||
|
self.matches = {}
|
||||||
|
self.style = styled('|', fg=opts.search_fg, bg=opts.search_bg).split('|', 1)[0]
|
||||||
|
if not is_regex:
|
||||||
|
query = re.escape(query)
|
||||||
|
try:
|
||||||
|
self.pat = re.compile(query, flags=re.UNICODE | re.IGNORECASE)
|
||||||
|
except Exception:
|
||||||
|
raise BadRegex('Not a valid regex: {}'.format(query))
|
||||||
|
|
||||||
|
def __call__(self, diff_lines, margin_size, cols):
|
||||||
|
self.matches = {}
|
||||||
|
half_width = cols // 2
|
||||||
|
strip_pat = re.compile('\033[[].*?m')
|
||||||
|
right_offset = half_width + 1 + margin_size
|
||||||
|
find = self.pat.finditer
|
||||||
|
for i, line in enumerate(diff_lines):
|
||||||
|
text = strip_pat.sub('', line.text)
|
||||||
|
left, right = text[margin_size:half_width + 1], text[right_offset:]
|
||||||
|
matches = []
|
||||||
|
|
||||||
|
def add(which, offset):
|
||||||
|
for m in find(which):
|
||||||
|
before = which[:m.start()]
|
||||||
|
matches.append((wcswidth(before) + offset, m.group()))
|
||||||
|
|
||||||
|
add(left, margin_size)
|
||||||
|
add(right, right_offset)
|
||||||
|
if matches:
|
||||||
|
self.matches[i] = matches
|
||||||
|
return bool(self.matches)
|
||||||
|
|
||||||
|
def __contains__(self, i):
|
||||||
|
return i in self.matches
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.matches)
|
||||||
|
|
||||||
|
def highlight_line(self, write, line_num):
|
||||||
|
highlights = self.matches.get(line_num)
|
||||||
|
if not highlights:
|
||||||
|
return False
|
||||||
|
write(self.style)
|
||||||
|
for start, text in highlights:
|
||||||
|
write('\r\x1b[{}C{}'.format(start, text))
|
||||||
|
write('\x1b[m')
|
||||||
|
return True
|
||||||
@ -30,7 +30,7 @@ class LineEdit:
|
|||||||
write(self.current_input)
|
write(self.current_input)
|
||||||
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
|
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
|
||||||
|
|
||||||
def on_text(self, text, in_bracketed_paste):
|
def add_text(self, text):
|
||||||
if self.current_input:
|
if self.current_input:
|
||||||
x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0
|
x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0
|
||||||
self.current_input = self.current_input[:x] + text + self.current_input[x:]
|
self.current_input = self.current_input[:x] + text + self.current_input[x:]
|
||||||
@ -38,6 +38,9 @@ class LineEdit:
|
|||||||
self.current_input = text
|
self.current_input = text
|
||||||
self.cursor_pos += wcswidth(text)
|
self.cursor_pos += wcswidth(text)
|
||||||
|
|
||||||
|
def on_text(self, text, in_bracketed_paste):
|
||||||
|
self.add_text(text)
|
||||||
|
|
||||||
def backspace(self, num=1):
|
def backspace(self, num=1):
|
||||||
before, after = self.split_at_cursor()
|
before, after = self.split_at_cursor()
|
||||||
nbefore = before[:-num]
|
nbefore = before[:-num]
|
||||||
|
|||||||
@ -78,6 +78,13 @@ def set_cursor_position(x, y) -> str: # (0, 0) is top left
|
|||||||
return '\033[{};{}H'.format(y + 1, x + 1)
|
return '\033[{};{}H'.format(y + 1, x + 1)
|
||||||
|
|
||||||
|
|
||||||
|
def set_cursor_shape(shape='block', blink=True) -> str:
|
||||||
|
val = {'block': 1, 'underline': 3, 'bar': 5}.get(shape, 1)
|
||||||
|
if not blink:
|
||||||
|
val += 1
|
||||||
|
return '\033[{} q'.format(val)
|
||||||
|
|
||||||
|
|
||||||
def set_scrolling_region(screen_size=None, top=None, bottom=None) -> str:
|
def set_scrolling_region(screen_size=None, top=None, bottom=None) -> str:
|
||||||
if screen_size is None:
|
if screen_size is None:
|
||||||
return '\033[r'
|
return '\033[r'
|
||||||
@ -220,12 +227,16 @@ def alternate_screen(f=None):
|
|||||||
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
|
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
|
||||||
|
|
||||||
|
|
||||||
def set_default_colors(fg=None, bg=None) -> str:
|
def set_default_colors(fg=None, bg=None, cursor=None) -> str:
|
||||||
ans = ''
|
ans = ''
|
||||||
if fg is None:
|
if fg is None:
|
||||||
ans += '\x1b]110\x1b\\'
|
ans += '\x1b]110\x1b\\'
|
||||||
else:
|
else:
|
||||||
ans += '\x1b]10;{}\x1b\\'.format(color_as_sharp(fg if isinstance(fg, Color) else to_color(fg)))
|
ans += '\x1b]10;{}\x1b\\'.format(color_as_sharp(fg if isinstance(fg, Color) else to_color(fg)))
|
||||||
|
if cursor is None:
|
||||||
|
ans += '\x1b]112\x1b\\'
|
||||||
|
else:
|
||||||
|
ans += '\x1b]12;{}\x1b\\'.format(color_as_sharp(cursor if isinstance(cursor, Color) else to_color(cursor)))
|
||||||
if bg is None:
|
if bg is None:
|
||||||
ans += '\x1b]111\x1b\\'
|
ans += '\x1b]111\x1b\\'
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user