Skip to content

Commit

Permalink
[sidebar] Ctrl+G to cycle in both main and input modes #2202
Browse files Browse the repository at this point in the history
  • Loading branch information
saulpw committed Aug 31, 2024
1 parent 9979479 commit ac2ec2a
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 58 deletions.
48 changes: 9 additions & 39 deletions visidata/_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
vd.theme_option('disp_unprintable', '·', 'substitute character for unprintables')
vd.theme_option('mouse_interval', 1, 'max time between press/release for click (ms)', sheettype=None)

vd.disp_help = 1 # current level of help shown (up to vd.options.disp_help as maximum)
vd.disp_help = 0 # current page of help shown
vd._help_sidebars = [] # list of (help:str|HelpPane, title:str)


class AcceptInput(Exception):
'*args[0]* is the input to be accepted'
Expand Down Expand Up @@ -89,47 +91,16 @@ def splice(v:str, i:int, s:str):
return v if i < 0 else v[:i] + s + v[i:]


# vd.options.disp_help is the effective maximum disp_help. The user can cycle through the various levels of help.
class HelpCycler:
def __init__(self, scr=None, help=''):
self.help = help
self.scr = scr

def __enter__(self):
self.draw()

return self

def __exit__(self, *args):
pass

def cycle(self):
vd.disp_help = (vd.disp_help-1)%(vd.options.disp_help+1)
self.draw()

def draw(self):
if self.scr:
vd.drawInputHelp(self.scr, self.help)


@VisiData.api
def drawInputHelp(vd, scr, help:str=''):
def drawInputHelp(vd, scr):
if not scr or not vd.cursesEnabled:
return

sheet = vd.activeSheet
if not sheet:
return

curhelp = ''
if vd.disp_help == 0:
vd.drawSidebar(scr, sheet)
elif vd.disp_help == 1:
curhelp = help
sheet.drawSidebarText(scr, curhelp)
elif vd.disp_help >= 2:
curhelp = vd.getHelpPane('input', module='visidata')
sheet.drawSidebarText(scr, curhelp, title='Input Keystrokes Help')
vd.drawSidebar(scr, sheet)


def clean_printable(s):
Expand Down Expand Up @@ -223,7 +194,7 @@ def editline(vd, scr, y, x, w, i=0,
If *clear* is True, clear whole editing area before displaying.
'''
with EnableCursor():
with HelpCycler(scr, help) as disp_help:
with vd.AddedHelp(vd.getHelpPane('input', module='visidata'), 'Input Keystrokes Help'), vd.AddedHelp(help, 'Input Field Help'):
ESC='^['
TAB='^I'
history_state = HistoryState(history)
Expand Down Expand Up @@ -259,7 +230,7 @@ def find_nonword(s, a, b, incr):
while True:
vd.drawSheet(scr, vd.activeSheet)
updater(v)
disp_help.draw()
vd.drawInputHelp(scr)

if display:
dispval = clean_printable(v)
Expand Down Expand Up @@ -296,7 +267,7 @@ def find_nonword(s, a, b, incr):
elif ch == '^E' or ch == 'KEY_END': i = len(v)
elif ch == '^F' or ch == 'KEY_RIGHT': i += 1
elif ch == '^G':
disp_help.cycle()
vd.cycleSidebar()
continue # not considered a first keypress
elif ch in ('^H', 'KEY_BACKSPACE', '^?'): i -= 1; v = delchar(v, i)
elif ch == TAB: v, i = complete_state.complete(v, i, +1)
Expand Down Expand Up @@ -459,8 +430,7 @@ def _drawPrompt(val):

return updater(val)

with HelpCycler() as disp_help:
while True:
while True:
try:
input_kwargs = kwargs[cur_input_key]
input_kwargs['value'] = vd.input(**input_kwargs,
Expand Down
2 changes: 1 addition & 1 deletion visidata/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from visidata import VisiData, MetaSheet, ColumnAttr, Column, BaseSheet, VisiDataMetaSheet, SuspendCurses
from visidata import vd, asyncthread, ENTER, drawcache, AttrDict, TextSheet

vd.option('disp_help', 2, 'show help panel during input')
vd.option('disp_expert', 2, 'max level of options and columns to include')

@BaseSheet.api
def hint_basichelp(sheet):
Expand Down
2 changes: 1 addition & 1 deletion visidata/mainloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def mainloop(vd, scr):
numTimeouts = 0
prefixWaiting = False
vd.scrFull = scr
vd.disp_help = vd.options.disp_help
vd.disp_help = -1 if vd.options.disp_expert else 10

vd.keystrokes = ''
vd.drawThread = threading.current_thread()
Expand Down
4 changes: 2 additions & 2 deletions visidata/sheets.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def resetCols(self):
self.columns = []
for c in self.initialCols:
self.addColumn(deepcopy(c))
if self.options.disp_help > c.max_help:
if self.options.disp_expert > c.max_help:
c.hide()

self.setKeys(self.columns[:self.nKeys])
Expand Down Expand Up @@ -1121,7 +1121,7 @@ def _async_deepcopy(newlist, oldlist):


BaseSheet.addCommand('^R', 'reload-sheet', 'preloadHook(); reload()', 'Reload current sheet')
Sheet.addCommand('^G', 'show-cursor', 'status(statusLine)', 'show cursor position and bounds of current sheet on status line')
Sheet.addCommand('', 'show-cursor', 'status(statusLine)', 'show cursor position and bounds of current sheet on status line')

Sheet.addCommand('!', 'key-col', 'toggleKeys([cursorCol])', 'toggle current column as a key column')
Sheet.addCommand('z!', 'key-col-off', 'unsetKeys([cursorCol])', 'unset current column as a key column')
Expand Down
82 changes: 68 additions & 14 deletions visidata/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,74 @@
vd.theme_option('color_sidebar', 'black on 114 blue', 'base color of sidebar')
vd.theme_option('color_sidebar_title', 'black on yellow', 'color of sidebar title')

@VisiData.api
class AddedHelp:
'''Context manager to add help text/screen to list of available sidebars.'''
def __init__(self, text:Union[str,'HelpPane'], title=''):
if text:
self.helpfunc = lambda: (text, title)
else:
self.helpfunc = None

def __enter__(self):
if self.helpfunc:
vd._help_sidebars.insert(0, self.helpfunc)
vd.clearCaches()
return self

def __exit__(self, *args):
if self.helpfunc:
vd._help_sidebars.remove(self.helpfunc)
vd.clearCaches()


@BaseSheet.property
def formatter_helpstr(sheet):
return AttrDict(commands=CommandHelpGetter(type(sheet)),
options=OptionHelpGetter())


@BaseSheet.property
@BaseSheet.cached_property
def default_sidebar(sheet):
'Default to format options.disp_sidebar_fmt. Overridable.'
fmt = sheet.options.disp_sidebar_fmt
return sheet.formatString(fmt, help=sheet.formatter_helpstr)


@VisiData.api
def cycleSidebar(vd, n:int=1):
if vd.sheet.help_sidebars:
vd.disp_help += n
if vd.disp_help >= len(vd.sheet.help_sidebars):
vd.disp_help = -1

vd.options.disp_sidebar = (vd.disp_help >= 0)
else:
vd.disp_help = -1
vd.clearCaches()


@BaseSheet.cached_property
def help_sidebars(sheet) -> 'list[Callable[[], tuple[str,str]]]':
r = []
if sheet.default_sidebar:
r.append(lambda: (sheet.default_sidebar, ''))
if sheet.guide:
r.append(lambda: (sheet.formatString(sheet.guide, help=sheet.formatter_helpstr), ''))
return r + vd._help_sidebars


@VisiData.cached_property
def sidebarStatus(vd) -> str:
if vd.sheet.help_sidebars:
if vd.options.disp_sidebar and vd.disp_help >= 0:
n = vd.disp_help+1
return f'[:onclick sidebar-toggle][:sidebar][{n}/{len(vd.sheet.help_sidebars)}][/]'
else:
return f'[:onclick sidebar-toggle][:sidebar][{len(vd.sheet.help_sidebars)}][/]'

return ''

@VisiData.property
def recentStatusMessages(vd) -> str:
r = ''
Expand All @@ -51,27 +106,24 @@ def recentStatusMessages(vd) -> str:
@VisiData.api
def drawSidebar(vd, scr, sheet):
sidebar = vd.recentStatusMessages
title = ''
bottommsg = ''
overflowmsg = '[:reverse] Ctrl+P to view all status messages [/]'
try:
if not sidebar and sheet.options.disp_sidebar:
sidebar = sheet.default_sidebar
if not sidebar and sheet.options.disp_help > 0:
sidebar = sheet.formatString(sheet.guide, help=sheet.formatter_helpstr)

if sheet.options.disp_help < 0:
bottommsg = '[:onclick sidebar-toggle][:reverse][x][:]'
overflowmsg = '[:onclick open-sidebar]…↓…[/]'
else:
bottommsg = sheet.formatString('[:onclick sidebar-toggle][:reverse] {help.commands.sidebar_toggle} [:]', help=sheet.formatter_helpstr)
overflowmsg = '[:reverse] see full sidebar with [:code]gb[/] [:]'
if not sidebar and vd.options.disp_sidebar and vd.disp_help >= 0:
sidebar, title = sheet.help_sidebars[vd.disp_help%len(sheet.help_sidebars)]()

# bottommsg = sheet.formatString('[:onclick sidebar-toggle][:reverse] {help.commands.sidebar_toggle} [:]', help=sheet.formatter_helpstr)
# overflowmsg = '[:reverse] see full sidebar with [:code]gb[/] [:]'

overflowmsg = '[:onclick open-sidebar]…↓…[/]'
except Exception as e:
vd.exceptionCaught(e)
sidebar = f'# error\n{e}'

sheet.current_sidebar = sidebar

return sheet.drawSidebarText(scr, text=sheet.current_sidebar, overflowmsg=overflowmsg, bottommsg=bottommsg)
return sheet.drawSidebarText(scr, text=sheet.current_sidebar, title=title, overflowmsg=overflowmsg, bottommsg=bottommsg)

@BaseSheet.api
def drawSidebarText(sheet, scr, text:Union[None,str,'HelpPane'], title:str='', overflowmsg:str='', bottommsg:str=''):
Expand Down Expand Up @@ -152,11 +204,13 @@ class SidebarSheet(TextSheet):
- `b` to toggle the sidebar on/off for the current sheet
'''

BaseSheet.addCommand('b', 'sidebar-toggle', 'sheet.options.disp_sidebar = not sheet.options.disp_sidebar', 'toggle sidebar')
BaseSheet.addCommand('b', 'sidebar-toggle', 'vd.options.disp_sidebar = not vd.options.disp_sidebar', 'toggle sidebar')
BaseSheet.addCommand('gb', 'open-sidebar', 'sheet.current_sidebar = "" if not hasattr(sheet, "current_sidebar") else sheet.current_sidebar; vd.push(SidebarSheet(name, options.disp_sidebar_fmt, source=sheet.current_sidebar.splitlines()))', 'open sidebar in new sheet')
BaseSheet.addCommand('^G', 'sidebar-cycle', 'vd.cycleSidebar()', 'cycle through available sidebar panels')


vd.addMenuItems('''
View > Sidebar > toggle > sidebar-toggle
View > Sidebar > cycle > sidebar-cycle
View > Sidebar > open in new sheet > open-sidebar
''')
3 changes: 2 additions & 1 deletion visidata/statusbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from visidata import vd, VisiData, BaseSheet, Sheet, ColumnItem, Column, RowColorizer, options, colors, wrmap, clipdraw, ExpectedException, update_attr, dispwidth, ColorAttr


vd.option('disp_rstatus_fmt', '{sheet.threadStatus} {sheet.keystrokeStatus} [:longname_status]{sheet.longname}[/] {sheet.nRows:9d} {sheet.rowtype} {sheet.modifiedStatus}{sheet.selectedStatus}{vd.replayStatus}', 'right-side status format string')

vd.option('disp_rstatus_fmt', '{sheet.threadStatus} {sheet.keystrokeStatus} [:longname_status]{sheet.longname}[/] {sheet.nRows:9d} {sheet.rowtype} {sheet.modifiedStatus}{sheet.selectedStatus}{vd.replayStatus}{vd.sidebarStatus}', 'right-side status format string')
vd.option('disp_status_fmt', '{sheet.sheetlist}| ', 'left-side status format string')
vd.theme_option('disp_lstatus_max', 0, 'maximum length of left status line')
vd.theme_option('disp_status_sep', '│', 'separator between statuses')
Expand Down

0 comments on commit ac2ec2a

Please sign in to comment.