From 13cc49cf05d0341767b506b55b8acc110bc399ca Mon Sep 17 00:00:00 2001 From: Saul Pwanson Date: Sun, 3 Nov 2024 16:58:50 -0800 Subject: [PATCH] [keys] use prettykeys internally for input bindings and other getkeystroke uses --- visidata/_input.py | 84 ++++++++++++++++----------------- visidata/canvas_text.py | 24 +++++----- visidata/clipboard.py | 4 +- visidata/features/cmdpalette.py | 12 ++--- visidata/features/helloworld.py | 2 +- visidata/form.py | 2 +- visidata/help.py | 8 ++-- visidata/mainloop.py | 1 - visidata/menu.py | 16 +++---- visidata/movement.py | 42 ++++++++--------- visidata/tests/test_commands.py | 4 +- visidata/tests/test_edittext.py | 38 +++++++-------- visidata/vdobj.py | 4 +- 13 files changed, 119 insertions(+), 122 deletions(-) diff --git a/visidata/_input.py b/visidata/_input.py index 0bef336e5..5cf46daf2 100644 --- a/visidata/_input.py +++ b/visidata/_input.py @@ -227,29 +227,29 @@ def handle_key(self, ch:str, scr) -> bool: v = self.value if ch == '': return False - elif ch == 'KEY_IC': self.insert_mode = not self.insert_mode - elif ch == '^A' or ch == 'KEY_HOME': i = 0 - elif ch == '^B' or ch == 'KEY_LEFT': i -= 1 - elif ch in ('^C', '^Q', '^['): raise EscapeException(ch) - elif ch == '^D' or ch == 'KEY_DC': v = delchar(v, i) - elif ch == '^E' or ch == 'KEY_END': i = len(v) - elif ch == '^F' or ch == 'KEY_RIGHT': i += 1 - elif ch == '^G': + elif ch == 'Ins': self.insert_mode = not self.insert_mode + elif ch == 'Ctrl+A' or ch == 'Home': i = 0 + elif ch == 'Ctrl+B' or ch == 'Left': i -= 1 + elif ch in ('Ctrl+C', 'Ctrl+Q', 'Ctrl+['): raise EscapeException(ch) + elif ch == 'Ctrl+D' or ch == 'Del': v = delchar(v, i) + elif ch == 'Ctrl+E' or ch == 'End': i = len(v) + elif ch == 'Ctrl+F' or ch == 'Right': i += 1 + elif ch == 'Ctrl+G': vd.cycleSidebar() return False # not considered a first keypress - elif ch in ('^H', 'KEY_BACKSPACE', '^?'): i -= 1; v = delchar(v, i) - elif ch == '^I': v, i = self.completion(v, i, +1) - elif ch == 'KEY_BTAB': v, i = self.completion(v, i, -1) - elif ch in ['^J', '^M']: return True # ENTER to accept value - elif ch == '^K': v = v[:i] # ^Kill to end-of-line - elif ch == '^N': + elif ch in ('Ctrl+H', 'Bksp', 'Ctrl+?'): i -= 1; v = delchar(v, i) + elif ch == 'Tab': v, i = self.completion(v, i, +1) + elif ch == 'Shift+Tab': v, i = self.completion(v, i, -1) + elif ch == 'Enter': return True # ENTER to accept value + elif ch == 'Ctrl+K': v = v[:i] # Ctrl+Kill to end-of-line + elif ch == 'Ctrl+N': c = '' while not c: c = vd.getkeystroke(scr) c = vd.prettykeys(c) i += len(c) v += c - elif ch == '^O': + elif ch == 'Ctrl+O': edit_v = vd.launchExternalEditor(v) if self.value == '' and edit_v == '': # if a cell has a value of None, keep it when the editor exits with no change @@ -257,20 +257,20 @@ def handle_key(self, ch:str, scr) -> bool: else: self.value = edit_v return True - elif ch == '^R': v = self.orig_value # ^Reload initial value - elif ch == '^T': v = delchar(splice(v, i-2, v[i-1:i]), i) # swap chars - elif ch == '^U': v = v[i:]; i = 0 # clear to beginning - elif ch == '^V': v = splice(v, i, until_get_wch(scr)); i += 1 # literal character - elif ch == '^W': j = find_nonword(v, 0, i-1, -1); v = v[:j+1] + v[i:]; i = j+1 # erase word - elif ch == '^Y': v = splice(v, i, str(vd.memory.clipval)) - elif ch == '^Z': vd.suspend() + elif ch == 'Ctrl+R': v = self.orig_value # Ctrl+Reload initial value + elif ch == 'Ctrl+T': v = delchar(splice(v, i-2, v[i-1:i]), i) # swap chars + elif ch == 'Ctrl+U': v = v[i:]; i = 0 # clear to beginning + elif ch == 'Ctrl+V': v = splice(v, i, until_get_wch(scr)); i += 1 # literal character + elif ch == 'Ctrl+W': j = find_nonword(v, 0, i-1, -1); v = v[:j+1] + v[i:]; i = j+1 # erase word + elif ch == 'Ctrl+Y': v = splice(v, i, str(vd.memory.clipval)) + elif ch == 'Ctrl+Z': vd.suspend() # CTRL+arrow - elif ch == 'kLFT5': i = find_nonword(v, 0, i-1, -1)+1; # word left - elif ch == 'kRIT5': i = find_nonword(v, i+1, len(v)-1, +1)+1; # word right - elif ch == 'kUP5': pass - elif ch == 'kDN5': pass - elif self.history and ch == 'KEY_UP': v, i = self.prev_history(v, i) - elif self.history and ch == 'KEY_DOWN': v, i = self.next_history(v, i) + elif ch == 'Ctrl+Left': i = find_nonword(v, 0, i-1, -1)+1; # word left + elif ch == 'Ctrl+Right': i = find_nonword(v, i+1, len(v)-1, +1)+1; # word right + elif ch == 'Ctrl+Up': pass + elif ch == 'Ctrl+Down': pass + elif self.history and ch == 'Up': v, i = self.prev_history(v, i) + elif self.history and ch == 'Down': v, i = self.next_history(v, i) elif len(ch) > 1: pass else: if self.first_action: @@ -481,12 +481,10 @@ def _drawPrompt(val): updater=_drawPrompt, record=False, bindings={ - 'KEY_BTAB': change_input(-1), - '^I': change_input(+1), - 'KEY_SR': change_input(-1), - 'KEY_SF': change_input(+1), - 'kUP': change_input(-1), - 'kDN': change_input(+1), + 'Shift+Tab': change_input(-1), + 'Tab': change_input(+1), + 'Shift+Up': change_input(-1), + 'Shift+Down': change_input(+1), }) break except ChangeInput as e: @@ -623,21 +621,19 @@ def editCell(self, vcolidx=None, rowidx=None, value=None, **kwargs): value = value or col.getDisplayValue(self.rows[self.cursorRowIndex]) bindings={ - 'kUP': acceptThenFunc('go-up', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'KEY_SR': acceptThenFunc('go-up', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'kDN': acceptThenFunc('go-down', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'KEY_SF': acceptThenFunc('go-down', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'KEY_SRIGHT': acceptThenFunc('go-right', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'KEY_SLEFT': acceptThenFunc('go-left', 'rename-col' if rowidx < 0 else 'edit-cell'), - '^I': acceptThenFunc('go-right', 'rename-col' if rowidx < 0 else 'edit-cell'), - 'KEY_BTAB': acceptThenFunc('go-left', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Shift+Up': acceptThenFunc('go-up', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Shift+Down': acceptThenFunc('go-down', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Shift+Right': acceptThenFunc('go-right', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Shift+Left': acceptThenFunc('go-left', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Tab': acceptThenFunc('go-right', 'rename-col' if rowidx < 0 else 'edit-cell'), + 'Shift+Tab': acceptThenFunc('go-left', 'rename-col' if rowidx < 0 else 'edit-cell'), } if vcolidx >= self.nVisibleCols-1: - bindings['^I'] = acceptThenFunc('go-down', 'go-leftmost', 'edit-cell') + bindings['Tab'] = acceptThenFunc('go-down', 'go-leftmost', 'edit-cell') if vcolidx <= 0: - bindings['KEY_BTAB'] = acceptThenFunc('go-up', 'go-rightmost', 'edit-cell') + bindings['Shift+Tab'] = acceptThenFunc('go-up', 'go-rightmost', 'edit-cell') # update local bindings with kwargs.bindings instead of the inverse, to preserve kwargs.bindings for caller bindings.update(kwargs.get('bindings', {})) diff --git a/visidata/canvas_text.py b/visidata/canvas_text.py index 2b306a460..4b0ea544d 100644 --- a/visidata/canvas_text.py +++ b/visidata/canvas_text.py @@ -129,18 +129,18 @@ def slide(self, rows, dx, dy): TextCanvas.addCommand('', 'go-up', 'cursorBox.y1 -= 1') TextCanvas.addCommand('', 'go-left', 'cursorBox.x1 -= 1') TextCanvas.addCommand('', 'go-right', 'cursorBox.x1 += 1') -TextCanvas.addCommand('kRIT5', 'resize-cursor-wider', 'cursorBox.w += 1', 'increase cursor width by one character') -TextCanvas.addCommand('kLFT5', 'resize-cursor-thinner', 'cursorBox.w -= 1', 'decrease cursor width by one character') -TextCanvas.addCommand('kUP5', 'resize-cursor-shorter', 'cursorBox.h -= 1', 'decrease cursor height by one character') -TextCanvas.addCommand('kDN5', 'resize-cursor-taller', 'cursorBox.h += 1', 'increase cursor height by one character') -TextCanvas.addCommand('gzKEY_LEFT', 'resize-cursor-min-width', 'cursorBox.w = 1') -TextCanvas.addCommand('gzKEY_UP', 'resize-cursor-min-height', 'cursorBox.h = 1') +TextCanvas.addCommand('Ctrl+Right', 'resize-cursor-wider', 'cursorBox.w += 1', 'increase cursor width by one character') +TextCanvas.addCommand('Ctrl+Left', 'resize-cursor-thinner', 'cursorBox.w -= 1', 'decrease cursor width by one character') +TextCanvas.addCommand('Ctrl+Up', 'resize-cursor-shorter', 'cursorBox.h -= 1', 'decrease cursor height by one character') +TextCanvas.addCommand('Ctrl+Down', 'resize-cursor-taller', 'cursorBox.h += 1', 'increase cursor height by one character') +TextCanvas.addCommand('gzLeft', 'resize-cursor-min-width', 'cursorBox.w = 1') +TextCanvas.addCommand('gzUp', 'resize-cursor-min-height', 'cursorBox.h = 1') TextCanvas.addCommand('z_', 'resize-cursor-min', 'cursorBox.h = cursorBox.w = 1') TextCanvas.addCommand('g_', 'resize-cursor-max', 'cursorBox.x1=cursorBox.y1=0; cursorBox.h=maxY+1; cursorBox.w=maxX+1') -TextCanvas.bindkey('zKEY_RIGHT', 'resize-cursor-wider') -TextCanvas.bindkey('zKEY_LEFT', 'resize-cursor-thinner') -TextCanvas.bindkey('zKEY_UP', 'resize-cursor-shorter') -TextCanvas.bindkey('zKEY_DOWN', 'resize-cursor-taller') +TextCanvas.bindkey('zRight', 'resize-cursor-wider') +TextCanvas.bindkey('zLeft', 'resize-cursor-thinner') +TextCanvas.bindkey('zUp', 'resize-cursor-shorter') +TextCanvas.bindkey('zDown', 'resize-cursor-taller') TextCanvas.addCommand('BUTTON1_PRESSED', 'move-cursor', 'sheet.cursorBox = CharBox(None, mouseX, mouseY, 1, 1)', 'start cursor box with left mouse button press') TextCanvas.addCommand('BUTTON1_RELEASED', 'end-cursor', 'cursorBox.x2=mouseX+2; cursorBox.y2=mouseY+2; cursorBox.normalize()', 'end cursor box with left mouse button release') @@ -155,8 +155,8 @@ def slide(self, rows, dx, dy): TextCanvas.addCommand('zu', 'unselect-top-cursor', 'source.unselect(list(itercursor(n=1)))') TextCanvas.addCommand('d', 'delete-cursor', 'source.deleteBy(lambda r,rows=cursorRows: r in rows)', 'delete first item under cursor') TextCanvas.addCommand('gd', 'delete-selected', 'source.deleteSelected()', 'delete selected rows on source sheet') -TextCanvas.addCommand(ENTER, 'dive-cursor', 'vs=copy(source); vs.rows=cursorRows; vs.source=sheet; vd.push(vs)', 'dive into source rows under cursor') -TextCanvas.addCommand('g'+ENTER, 'dive-selected', 'vd.push(type(source)(source=sheet, rows=source.selectedRows))', 'dive into selected source rows') +TextCanvas.addCommand('Enter', 'dive-cursor', 'vs=copy(source); vs.rows=cursorRows; vs.source=sheet; vd.push(vs)', 'dive into source rows under cursor') +TextCanvas.addCommand('gEnter', 'dive-selected', 'vd.push(type(source)(source=sheet, rows=source.selectedRows))', 'dive into selected source rows') TextCanvas.addCommand('H', 'slide-left-obj', 'slide(source.selectedRows, -1, 0)', 'slide selected objects left one character') TextCanvas.addCommand('J', 'slide-down-obj', 'slide(source.selectedRows, 0, +1)', 'slide selected objects down one character') diff --git a/visidata/clipboard.py b/visidata/clipboard.py index f8a109e9a..9e37ddba1 100644 --- a/visidata/clipboard.py +++ b/visidata/clipboard.py @@ -204,8 +204,8 @@ def paste_after(sheet, rowidx): Sheet.addCommand('gzx', 'cut-cells', 'copyCells(cursorCol, onlySelectedRows); cursorCol.setValues(onlySelectedRows, None)', 'delete (cut) contents of current column for selected rows and move them to clipboard') -Sheet.bindkey('KEY_DC', 'delete-cell'), -Sheet.bindkey('gKEY_DC', 'delete-cells'), +Sheet.bindkey('Del', 'delete-cell'), +Sheet.bindkey('gDel', 'delete-cells'), vd.addMenuItems(''' Edit > Delete > current row > delete-row diff --git a/visidata/features/cmdpalette.py b/visidata/features/cmdpalette.py index c6ae343b8..a8a07126d 100644 --- a/visidata/features/cmdpalette.py +++ b/visidata/features/cmdpalette.py @@ -109,8 +109,8 @@ def _draw_palette(value): navailitems = min(len(palrows), nitems) - bindings['^I'] = lambda *args: tab(1, navailitems) or args - bindings['KEY_BTAB'] = lambda *args: tab(-1, navailitems) or args + bindings['Ctrl+I'] = lambda *args: tab(1, navailitems) or args + bindings['Shift+Tab'] = lambda *args: tab(-1, navailitems) or args for i in range(nitems-len(palrows)): palrows.append((None, None)) @@ -132,16 +132,16 @@ def _draw_palette(value): if not topitem: return if multiple: bindings[' '] = partial(add_to_input, value=topitem[value_key]) - bindings['^J'] = partial(accept_input_if_subset, value=topitem[value_key]) + bindings['Enter'] = partial(accept_input_if_subset, value=topitem[value_key]) else: - bindings['^J'] = partial(accept_input, value=topitem[value_key]) + bindings['Enter'] = partial(accept_input, value=topitem[value_key]) elif item and i == tabitem: if not item: return if multiple: - bindings['^J'] = partial(accept_input_if_subset, value=item[value_key]) + bindings['Enter'] = partial(accept_input_if_subset, value=item[value_key]) bindings[' '] = partial(add_to_input, value=item[value_key]) else: - bindings['^J'] = partial(accept_input, value=item[value_key]) + bindings['Enter'] = partial(accept_input, value=item[value_key]) attr = colors.color_menu_spec match_summary = formatter(m, item, trigger_key) if item else ' ' diff --git a/visidata/features/helloworld.py b/visidata/features/helloworld.py index 75efce883..691e0032c 100644 --- a/visidata/features/helloworld.py +++ b/visidata/features/helloworld.py @@ -7,4 +7,4 @@ vd.option('hello_world', '¡Hola mundo!', 'shown by the hello-world command') -BaseSheet.addCommand('KEY_F(2)', 'hello-world', 'status(options.hello_world)', 'print greeting to status') +BaseSheet.addCommand('F2', 'hello-world', 'status(options.hello_world)', 'print greeting to status') diff --git a/visidata/form.py b/visidata/form.py index 2e019548f..dfe2c2c68 100644 --- a/visidata/form.py +++ b/visidata/form.py @@ -76,7 +76,7 @@ def run(self, scr): while True: k = vd.getkeystroke(self.scrForm, self) - if k in ['^C', '^Q', '^[', 'q']: + if k in ['Ctrl+C', 'Ctrl+Q', 'Ctrl+[', 'q']: return {} if curinput and k in curinput.keystrokes: return {curinput.input: k} diff --git a/visidata/help.py b/visidata/help.py index 2c8e1a46c..c8be9ce18 100644 --- a/visidata/help.py +++ b/visidata/help.py @@ -168,10 +168,10 @@ def openManPage(vd): BaseSheet.addCommand('z^H', 'help-commands', 'vd.push(HelpSheet(name + "_commands", source=sheet, revbinds={}))', 'list commands and keybindings available on current sheet') BaseSheet.addCommand('gz^H', 'help-commands-all', 'vd.push(HelpSheet("all_commands", source=None, revbinds={}))', 'list commands and keybindings for all sheet types') -BaseSheet.bindkey('KEY_F(1)', 'sysopen-help') -BaseSheet.bindkey('zKEY_F(1)', 'help-commands') -BaseSheet.bindkey('zKEY_BACKSPACE', 'help-commands') -BaseSheet.bindkey('gKEY_BACKSPACE', 'sysopen-help') +BaseSheet.bindkey('F1', 'sysopen-help') +BaseSheet.bindkey('zF1', 'help-commands') +BaseSheet.bindkey('zBksp', 'help-commands') +BaseSheet.bindkey('gBksp', 'sysopen-help') HelpSheet.addCommand(None, 'exec-command', 'quit(sheet); draw_all(); activeStack[0].execCommand(cursorRow.longname)', 'execute command on undersheet') BaseSheet.addCommand(None, 'open-tutorial-visidata', 'launchBrowser("https://jsvine.github.io/intro-to-visidata/")', 'open https://jsvine.github.io/intro-to-visidata/') diff --git a/visidata/mainloop.py b/visidata/mainloop.py index 7b986306e..861e1c5d1 100644 --- a/visidata/mainloop.py +++ b/visidata/mainloop.py @@ -212,7 +212,6 @@ def mainloop(vd, scr): vd.warning('duplicate prefix: ' + keystroke) vd.keystrokes = '' else: - keystroke = vd.prettykeys(keystroke) vd.keystrokes += keystroke vd.drawRightStatus(sheet._scr, sheet) # visible for commands that wait for input diff --git a/visidata/menu.py b/visidata/menu.py index 8e2b6d51f..ea0dae537 100644 --- a/visidata/menu.py +++ b/visidata/menu.py @@ -442,7 +442,7 @@ def _clickedDuringMenu(): currentItem = sheet.getMenuItem() - if k == '^[': # ESC + if k == 'Ctrl+[': # ESC nEscapes += 1 #1470 if nEscapes > 1: return @@ -450,7 +450,7 @@ def _clickedDuringMenu(): else: nEscapes = 0 - if k in ['^C', '^Q', 'q', '^H', 'KEY_BACKSPACE']: + if k in ['Ctrl+C', 'Ctrl+Q', 'q', 'Ctrl+H', 'Bksp']: return elif k in ['KEY_MOUSE']: @@ -460,25 +460,25 @@ def _clickedDuringMenu(): elif r == 'doit': break - elif k in ['KEY_RIGHT', 'l']: + elif k in ['Right', 'l']: if currentItem.menus and sheet.activeMenuItems[1] != 0: # not first item sheet.activeMenuItems.append(0) else: sheet.activeMenuItems = [sheet.activeMenuItems[0]+1, 0] - elif k in ['KEY_LEFT', 'h']: + elif k in ['Left', 'h']: if len(sheet.activeMenuItems) > 2: sheet.activeMenuItems.pop(-1) else: sheet.activeMenuItems = [sheet.activeMenuItems[0]-1, 0] - elif k in ['KEY_DOWN', 'j']: + elif k in ['Down', 'j']: sheet.activeMenuItems[-1] += 1 - elif k in ['KEY_UP', 'k']: + elif k in ['Up', 'k']: sheet.activeMenuItems[-1] -= 1 - elif k in [ENTER, ' ', '^J', '^M']: + elif k in ['Enter', ' ']: if currentItem.menus: sheet.activeMenuItems.append(0) else: @@ -512,6 +512,6 @@ def _clickedDuringMenu(): BaseSheet.addCommand('Alt+s', 'menu-system', 'pressMenu("System")', '') BaseSheet.addCommand('Alt+h', 'menu-help', 'pressMenu("Help")', 'open the Help menu') BaseSheet.bindkey('Ctrl+H', 'menu-help') -BaseSheet.bindkey('KEY_BACKSPACE', 'menu-help') +BaseSheet.bindkey('Bksp', 'menu-help') vd.addGlobals({'Menu': Menu}) diff --git a/visidata/movement.py b/visidata/movement.py index 55615f100..ceedd37a4 100644 --- a/visidata/movement.py +++ b/visidata/movement.py @@ -118,17 +118,17 @@ def visibleWidth(self): for i in range(11, 21): BaseSheet.addCommand('', 'jump-sheet-'+str(i), f'vd.push(*(list(s for s in allSheets if s.shortcut==str({i})) or fail("no sheet")))', f'jump to sheet {i}') -BaseSheet.bindkey('KEY_LEFT', 'go-left') -BaseSheet.bindkey('KEY_DOWN', 'go-down') -BaseSheet.bindkey('KEY_UP', 'go-up') -BaseSheet.bindkey('KEY_RIGHT', 'go-right') -BaseSheet.bindkey('KEY_NPAGE', 'go-pagedown') -BaseSheet.bindkey('KEY_PPAGE', 'go-pageup') - -BaseSheet.bindkey('gKEY_LEFT', 'go-leftmost'), -BaseSheet.bindkey('gKEY_RIGHT', 'go-rightmost'), -BaseSheet.bindkey('gKEY_UP', 'go-top'), -BaseSheet.bindkey('gKEY_DOWN', 'go-bottom'), +BaseSheet.bindkey('Left', 'go-left') +BaseSheet.bindkey('Down', 'go-down') +BaseSheet.bindkey('Up', 'go-up') +BaseSheet.bindkey('Right', 'go-right') +BaseSheet.bindkey('PgDn', 'go-pagedown') +BaseSheet.bindkey('PgUp', 'go-pageup') + +BaseSheet.bindkey('gLeft', 'go-leftmost'), +BaseSheet.bindkey('gRight', 'go-rightmost'), +BaseSheet.bindkey('gUp', 'go-top'), +BaseSheet.bindkey('gDown', 'go-bottom'), BaseSheet.bindkey('Home', 'go-top') BaseSheet.bindkey('End', 'go-bottom') @@ -138,8 +138,8 @@ def visibleWidth(self): # vim-style scrolling with the 'z' prefix Sheet.addCommand('zz', 'scroll-middle', 'sheet.topRowIndex = cursorRowIndex-int(nScreenRows/2)', 'scroll current row to center of screen') -Sheet.addCommand('kRIT5', 'go-right-page', 'sheet.cursorVisibleColIndex = sheet.leftVisibleColIndex = rightVisibleColIndex', 'scroll cursor one page right', replay=False) -Sheet.addCommand('kLFT5', 'go-left-page', 'pageLeft()', 'scroll cursor one page left', replay=False) +Sheet.addCommand('Ctrl+Right', 'go-right-page', 'sheet.cursorVisibleColIndex = sheet.leftVisibleColIndex = rightVisibleColIndex', 'scroll cursor one page right', replay=False) +Sheet.addCommand('Ctrl+Left', 'go-left-page', 'pageLeft()', 'scroll cursor one page left', replay=False) Sheet.addCommand(None, 'scroll-left', 'sheet.cursorVisibleColIndex -= options.scroll_incr', 'scroll one column left') Sheet.addCommand(None, 'scroll-right', 'sheet.cursorVisibleColIndex += options.scroll_incr', 'scroll one column right') Sheet.addCommand(None, 'scroll-leftmost', 'sheet.leftVisibleColIndex = cursorVisibleColIndex', 'scroll sheet to leftmost column') @@ -161,10 +161,10 @@ def visibleWidth(self): BaseSheet.bindkey('Ctrl+ScrollUp', 'scroll-left') BaseSheet.bindkey('Ctrl+ScrollDown', 'scroll-right') -BaseSheet.bindkey('zKEY_UP', 'scroll-up') -BaseSheet.bindkey('zKEY_DOWN', 'scroll-down') -BaseSheet.bindkey('zKEY_LEFT', 'scroll-left') -BaseSheet.bindkey('zKEY_RIGHT', 'scroll-right') +BaseSheet.bindkey('zUp', 'scroll-up') +BaseSheet.bindkey('zDown', 'scroll-down') +BaseSheet.bindkey('zLeft', 'scroll-left') +BaseSheet.bindkey('zRight', 'scroll-right') # vim-like keybindings @@ -172,8 +172,8 @@ def visibleWidth(self): BaseSheet.bindkey('j', 'go-down'), BaseSheet.bindkey('k', 'go-up'), BaseSheet.bindkey('l', 'go-right'), -BaseSheet.bindkey('^F', 'go-pagedown'), -BaseSheet.bindkey('^B', 'go-pageup'), +BaseSheet.bindkey('Ctrl+F', 'go-pagedown'), +BaseSheet.bindkey('Ctrl+B', 'go-pageup'), BaseSheet.bindkey('gg', 'go-top'), BaseSheet.bindkey('G', 'go-bottom'), BaseSheet.bindkey('gj', 'go-bottom'), @@ -181,8 +181,8 @@ def visibleWidth(self): BaseSheet.bindkey('gh', 'go-leftmost'), BaseSheet.bindkey('gl', 'go-rightmost') -BaseSheet.addCommand('^^', 'jump-prev', 'vd.activeStack[1:] or fail("no previous sheet"); vd.push(vd.activeStack[1])', 'jump to previous sheet in this pane') -BaseSheet.addCommand('g^^', 'jump-first', 'vd.push(vd.activeStack[-1])', 'jump to first sheet') +BaseSheet.addCommand('Ctrl+^', 'jump-prev', 'vd.activeStack[1:] or fail("no previous sheet"); vd.push(vd.activeStack[1])', 'jump to previous sheet in this pane') +BaseSheet.addCommand('gCtrl+^', 'jump-first', 'vd.push(vd.activeStack[-1])', 'jump to first sheet') BaseSheet.addCommand('BUTTON1_RELEASED', 'no-op', 'pass', 'do nothing') diff --git a/visidata/tests/test_commands.py b/visidata/tests/test_commands.py index d47a26050..f06d4128d 100644 --- a/visidata/tests/test_commands.py +++ b/visidata/tests/test_commands.py @@ -166,10 +166,10 @@ def runOneTest(self, mock_screen, longname): vd.scr = mock_screen if longname in inputLines: - line = [ch for ch in inputLines[longname]] + ['^J'] + line = [ch for ch in inputLines[longname]] + ['Enter'] vd.getkeystroke = Mock(side_effect=line) else: - vd.getkeystroke = Mock(side_effect=['^J']) + vd.getkeystroke = Mock(side_effect=['Enter']) sample_file = vd.pkg_resources_files(visidata) / 'tests/sample.tsv' vs = visidata.TsvSheet('test_commands', source=visidata.Path(sample_file)) diff --git a/visidata/tests/test_edittext.py b/visidata/tests/test_edittext.py index 4018382d4..df7070a63 100644 --- a/visidata/tests/test_edittext.py +++ b/visidata/tests/test_edittext.py @@ -11,36 +11,36 @@ def setUp(self): visidata.vd.getkeystroke = Mock(side_effect=self.chars) @pytest.mark.parametrize('keys, result, kwargs', [ - ('^J', '', {}), - ('a b KEY_HOME c d ^A e f ^J', 'efcdab', {}), - ('a b KEY_LEFT 1 KEY_LEFT KEY_LEFT KEY_LEFT 2 ^J', '2a1b', {}), # Left, past home - ('a b ^C', None, dict(exception=visidata.EscapeException)), - ('a b ^[', None, dict(exception=visidata.EscapeException)), - ('a KEY_DC ^J', 'a', {}), - ('a b KEY_LEFT KEY_DC ^J', 'a', {}), - ('a b KEY_LEFT c KEY_END d ^J', 'acbd', {}), - ('a b KEY_HOME KEY_RIGHT c ^J', 'acb', {}), - ('a b KEY_BACKSPACE c ^J', 'ac', {}), + ('Enter', '', {}), + ('a b Home c d Ctrl+A e f Enter', 'efcdab', {}), + ('a b Left 1 Left Left Left 2 Enter', '2a1b', {}), # Left, past home + ('a b Ctrl+C', None, dict(exception=visidata.EscapeException)), + ('a b Ctrl+[', None, dict(exception=visidata.EscapeException)), + ('a Del Enter', 'a', {}), + ('a b Left Del Enter', 'a', {}), + ('a b Left c End d Enter', 'acbd', {}), + ('a b Home Right c Enter', 'acb', {}), + ('a b Bksp c Enter', 'ac', {}), # Backspace no longer deletes the first character at the start - ('a b KEY_HOME KEY_BACKSPACE c ^J', 'cab', {}), + ('a b Home Bksp c Enter', 'cab', {}), # Backspace works in different combos, including on the mac. - ('a b c KEY_BACKSPACE ^H KEY_LEFT KEY_DC ^J', '', {}), + ('a b c Bksp Ctrl+H Left Del Enter', '', {}), - ('a b c ^B ^B ^K ^J', 'a', {}), + ('a b c Ctrl+B Ctrl+B Ctrl+K Enter', 'a', {}), - ('a ^R ^J', '', {}), - ('a ^R ^J', 'foo', dict(value='foo')), + ('a Ctrl+R Enter', '', {}), + ('a Ctrl+R Enter', 'foo', dict(value='foo')), # Two characters swaps characters - ('a b ^T ^J', 'ba', {}), + ('a b Ctrl+T Enter', 'ba', {}), # Home with multiple characters acts like delete - ('a b KEY_HOME ^T ^J', 'b', {}), + ('a b Home Ctrl+T Enter', 'b', {}), - ('a b KEY_LEFT ^U ^J', 'b', {}), - ('a b ^U c ^J', 'c', {}), + ('a b Left Ctrl+U Enter', 'b', {}), + ('a b Ctrl+U c Enter', 'c', {}), ]) def test_keys(self, mock_screen, keys, result, kwargs): self.chars.extend(keys.split()) diff --git a/visidata/vdobj.py b/visidata/vdobj.py index f076d38f6..91c204440 100644 --- a/visidata/vdobj.py +++ b/visidata/vdobj.py @@ -159,7 +159,9 @@ def getkeystroke(self, scr, vs=None): if ord(k) >= 32 and ord(k) != 127: # 127 == DEL or ^? return k k = ord(k) - return curses.keyname(k).decode('utf-8') + + keyname = curses.keyname(k).decode('utf-8') + return self.prettykeys(keyname) @property def screenHeight(self):