visidata 2.11.1__py3-none-any.whl → 3.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. visidata/__init__.py +72 -91
  2. visidata/_input.py +259 -42
  3. visidata/_open.py +84 -29
  4. visidata/_types.py +21 -3
  5. visidata/_urlcache.py +17 -4
  6. visidata/aggregators.py +78 -25
  7. visidata/apps/__init__.py +0 -0
  8. visidata/apps/vdsql/__about__.py +8 -0
  9. visidata/apps/vdsql/__init__.py +5 -0
  10. visidata/apps/vdsql/__main__.py +27 -0
  11. visidata/apps/vdsql/_ibis.py +748 -0
  12. visidata/apps/vdsql/bigquery.py +61 -0
  13. visidata/apps/vdsql/clickhouse.py +53 -0
  14. visidata/apps/vdsql/setup.py +40 -0
  15. visidata/apps/vdsql/snowflake.py +67 -0
  16. visidata/apps/vgit/__init__.py +13 -0
  17. {vgit → visidata/apps/vgit}/blame.py +5 -2
  18. {vgit → visidata/apps/vgit}/branch.py +31 -16
  19. {vgit → visidata/apps/vgit}/config.py +3 -3
  20. visidata/apps/vgit/diff.py +169 -0
  21. visidata/apps/vgit/gitsheet.py +161 -0
  22. {vgit → visidata/apps/vgit}/grep.py +6 -5
  23. visidata/apps/vgit/log.py +81 -0
  24. {vgit → visidata/apps/vgit}/main.py +18 -5
  25. {vgit → visidata/apps/vgit}/remote.py +8 -4
  26. visidata/apps/vgit/repos.py +71 -0
  27. {vgit → visidata/apps/vgit}/setup.py +6 -4
  28. visidata/apps/vgit/stash.py +69 -0
  29. visidata/apps/vgit/status.py +204 -0
  30. {vgit → visidata/apps/vgit}/statusbar.py +2 -0
  31. visidata/basesheet.py +63 -51
  32. visidata/canvas.py +208 -93
  33. visidata/choose.py +6 -6
  34. visidata/clean_names.py +29 -0
  35. visidata/clipboard.py +73 -17
  36. visidata/cliptext.py +220 -46
  37. visidata/cmdlog.py +88 -114
  38. visidata/color.py +142 -56
  39. visidata/column.py +121 -129
  40. visidata/ddw/input.ddw +74 -79
  41. visidata/ddw/regex.ddw +57 -0
  42. visidata/ddwplay.py +33 -14
  43. visidata/deprecated.py +77 -3
  44. visidata/desktop/visidata.desktop +7 -0
  45. visidata/editor.py +12 -6
  46. visidata/errors.py +6 -2
  47. visidata/experimental/__init__.py +0 -0
  48. visidata/experimental/diff_sheet.py +29 -0
  49. visidata/experimental/digit_autoedit.py +6 -0
  50. visidata/experimental/gdrive.py +89 -0
  51. visidata/experimental/google.py +37 -0
  52. visidata/experimental/gsheets.py +79 -0
  53. visidata/experimental/live_search.py +37 -0
  54. visidata/experimental/liveupdate.py +45 -0
  55. visidata/experimental/mark.py +133 -0
  56. visidata/experimental/noahs_tapestry/__init__.py +1 -0
  57. visidata/experimental/noahs_tapestry/tapestry.py +147 -0
  58. visidata/experimental/rownum.py +73 -0
  59. visidata/experimental/slide_cells.py +26 -0
  60. visidata/expr.py +8 -4
  61. visidata/extensible.py +22 -4
  62. visidata/features/__init__.py +0 -0
  63. visidata/features/addcol_audiometadata.py +42 -0
  64. visidata/features/addcol_histogram.py +34 -0
  65. visidata/features/canvas_save_svg.py +69 -0
  66. visidata/features/change_precision.py +46 -0
  67. visidata/features/cmdpalette.py +197 -0
  68. visidata/features/colorbrewer.py +363 -0
  69. visidata/{colorsheet.py → features/colorsheet.py} +17 -16
  70. visidata/features/command_server.py +105 -0
  71. visidata/features/currency_to_usd.py +70 -0
  72. visidata/{customdate.py → features/customdate.py} +2 -0
  73. visidata/features/dedupe.py +132 -0
  74. visidata/{describe.py → features/describe.py} +17 -15
  75. visidata/features/errors_guide.py +26 -0
  76. visidata/features/expand_cols.py +202 -0
  77. visidata/{fill.py → features/fill.py} +3 -1
  78. visidata/{freeze.py → features/freeze.py} +11 -6
  79. visidata/features/graph_seaborn.py +79 -0
  80. visidata/features/helloworld.py +10 -0
  81. visidata/features/hint_types.py +17 -0
  82. visidata/{incr.py → features/incr.py} +5 -0
  83. visidata/{join.py → features/join.py} +107 -53
  84. visidata/features/known_cols.py +21 -0
  85. visidata/features/layout.py +62 -0
  86. visidata/{melt.py → features/melt.py} +32 -21
  87. visidata/features/normcol.py +118 -0
  88. visidata/features/open_config.py +7 -0
  89. visidata/features/open_syspaste.py +18 -0
  90. visidata/features/ping.py +157 -0
  91. visidata/features/procmgr.py +208 -0
  92. visidata/features/random_sample.py +6 -0
  93. visidata/{regex.py → features/regex.py} +47 -31
  94. visidata/features/reload_every.py +55 -0
  95. visidata/features/rename_col_cascade.py +30 -0
  96. visidata/features/scroll_context.py +60 -0
  97. visidata/features/select_equal_selected.py +11 -0
  98. visidata/features/setcol_fake.py +65 -0
  99. visidata/{slide.py → features/slide.py} +77 -21
  100. visidata/features/sparkline.py +48 -0
  101. visidata/features/status_source.py +20 -0
  102. visidata/{sysedit.py → features/sysedit.py} +2 -1
  103. visidata/features/sysopen_mailcap.py +46 -0
  104. visidata/features/term_extras.py +13 -0
  105. visidata/{transpose.py → features/transpose.py} +5 -4
  106. visidata/features/type_ipaddr.py +73 -0
  107. visidata/features/type_url.py +11 -0
  108. visidata/{unfurl.py → features/unfurl.py} +9 -9
  109. visidata/{window.py → features/window.py} +2 -2
  110. visidata/form.py +50 -21
  111. visidata/freqtbl.py +81 -33
  112. visidata/fuzzymatch.py +414 -0
  113. visidata/graph.py +105 -33
  114. visidata/guide.py +200 -0
  115. visidata/help.py +75 -44
  116. visidata/hint.py +39 -0
  117. visidata/indexsheet.py +109 -0
  118. visidata/input_history.py +55 -0
  119. visidata/interface.py +58 -0
  120. visidata/keys.py +20 -16
  121. visidata/loaders/__init__.py +9 -0
  122. visidata/loaders/_pandas.py +61 -21
  123. visidata/loaders/api_airtable.py +70 -0
  124. visidata/loaders/api_bitio.py +102 -0
  125. visidata/loaders/api_matrix.py +148 -0
  126. visidata/loaders/api_reddit.py +306 -0
  127. visidata/loaders/api_zulip.py +249 -0
  128. visidata/loaders/archive.py +41 -7
  129. visidata/loaders/arrow.py +7 -7
  130. visidata/loaders/conll.py +49 -0
  131. visidata/loaders/csv.py +25 -7
  132. visidata/loaders/eml.py +3 -4
  133. visidata/loaders/f5log.py +1204 -0
  134. visidata/loaders/fec.py +325 -0
  135. visidata/loaders/fixed_width.py +2 -4
  136. visidata/loaders/frictionless.py +3 -3
  137. visidata/loaders/geojson.py +8 -5
  138. visidata/loaders/google.py +48 -0
  139. visidata/loaders/graphviz.py +4 -4
  140. visidata/loaders/hdf5.py +4 -4
  141. visidata/loaders/html.py +54 -12
  142. visidata/loaders/http.py +84 -30
  143. visidata/loaders/imap.py +20 -10
  144. visidata/loaders/jrnl.py +52 -0
  145. visidata/loaders/json.py +83 -29
  146. visidata/loaders/jsonla.py +74 -0
  147. visidata/loaders/lsv.py +15 -11
  148. visidata/loaders/mailbox.py +40 -0
  149. visidata/loaders/markdown.py +1 -3
  150. visidata/loaders/mbtiles.py +4 -5
  151. visidata/loaders/mysql.py +11 -13
  152. visidata/loaders/npy.py +7 -7
  153. visidata/loaders/odf.py +4 -1
  154. visidata/loaders/orgmode.py +428 -0
  155. visidata/loaders/pandas_freqtbl.py +14 -20
  156. visidata/loaders/parquet.py +62 -6
  157. visidata/loaders/pcap.py +3 -3
  158. visidata/loaders/pdf.py +4 -3
  159. visidata/loaders/png.py +19 -13
  160. visidata/loaders/postgres.py +9 -8
  161. visidata/loaders/rec.py +7 -3
  162. visidata/loaders/s3.py +342 -0
  163. visidata/loaders/sas.py +5 -5
  164. visidata/loaders/scrape.py +186 -0
  165. visidata/loaders/shp.py +6 -5
  166. visidata/loaders/spss.py +5 -6
  167. visidata/loaders/sqlite.py +68 -28
  168. visidata/loaders/texttables.py +1 -1
  169. visidata/loaders/toml.py +60 -0
  170. visidata/loaders/tsv.py +61 -19
  171. visidata/loaders/ttf.py +19 -7
  172. visidata/loaders/unzip_http.py +6 -5
  173. visidata/loaders/usv.py +1 -1
  174. visidata/loaders/vcf.py +16 -16
  175. visidata/loaders/vds.py +10 -7
  176. visidata/loaders/vdx.py +30 -5
  177. visidata/loaders/xlsb.py +8 -1
  178. visidata/loaders/xlsx.py +145 -25
  179. visidata/loaders/xml.py +6 -3
  180. visidata/loaders/xword.py +4 -4
  181. visidata/loaders/yaml.py +15 -5
  182. visidata/macros.py +129 -42
  183. visidata/main.py +119 -94
  184. visidata/mainloop.py +101 -155
  185. visidata/man/parse_options.py +2 -2
  186. visidata/man/vd.1 +302 -149
  187. visidata/man/vd.txt +291 -154
  188. visidata/memory.py +3 -3
  189. visidata/menu.py +104 -423
  190. visidata/metasheets.py +59 -141
  191. visidata/modify.py +78 -23
  192. visidata/motd.py +3 -3
  193. visidata/mouse.py +137 -0
  194. visidata/movement.py +43 -35
  195. visidata/optionssheet.py +99 -0
  196. visidata/path.py +113 -32
  197. visidata/pivot.py +73 -47
  198. visidata/plugins.py +65 -192
  199. visidata/pyobj.py +55 -205
  200. visidata/rename_col.py +20 -0
  201. visidata/save.py +37 -20
  202. visidata/search.py +54 -10
  203. visidata/selection.py +84 -5
  204. visidata/settings.py +162 -25
  205. visidata/sheets.py +239 -260
  206. visidata/shell.py +51 -21
  207. visidata/sidebar.py +162 -0
  208. visidata/sort.py +11 -4
  209. visidata/statusbar.py +114 -104
  210. visidata/stored_list.py +43 -0
  211. visidata/stored_prop.py +38 -0
  212. visidata/tests/benchmark.csv +52 -0
  213. visidata/tests/conftest.py +3 -3
  214. visidata/tests/test_cliptext.py +39 -0
  215. visidata/tests/test_commands.py +65 -7
  216. visidata/tests/test_edittext.py +2 -2
  217. visidata/tests/test_features.py +28 -0
  218. visidata/tests/test_menu.py +14 -0
  219. visidata/tests/test_path.py +13 -4
  220. visidata/text_source.py +53 -0
  221. visidata/textsheet.py +10 -3
  222. visidata/theme.py +44 -0
  223. visidata/themes/__init__.py +0 -0
  224. visidata/themes/ascii8.py +84 -0
  225. visidata/themes/asciimono.py +84 -0
  226. visidata/themes/light.py +17 -0
  227. visidata/threads.py +89 -40
  228. visidata/tuiwin.py +22 -0
  229. visidata/type_currency.py +22 -3
  230. visidata/type_date.py +31 -9
  231. visidata/type_floatsi.py +5 -1
  232. visidata/undo.py +17 -5
  233. visidata/utils.py +106 -23
  234. visidata/vdobj.py +28 -17
  235. visidata/windows.py +10 -0
  236. visidata/wrappers.py +9 -3
  237. visidata-3.0.1.data/data/share/applications/visidata.desktop +7 -0
  238. {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/vd.1 +302 -149
  239. {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/visidata.1 +302 -149
  240. visidata-3.0.1.data/scripts/vd2to3.vdx +9 -0
  241. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/METADATA +12 -8
  242. visidata-3.0.1.dist-info/RECORD +258 -0
  243. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/WHEEL +1 -1
  244. vgit/__init__.py +0 -1
  245. vgit/gitsheet.py +0 -164
  246. visidata/layout.py +0 -44
  247. visidata/misc.py +0 -5
  248. visidata-2.11.1.data/scripts/vgit +0 -9
  249. visidata-2.11.1.dist-info/RECORD +0 -155
  250. {vgit → visidata/apps/vgit}/__main__.py +0 -0
  251. {vgit → visidata/apps/vgit}/abort.py +0 -0
  252. /visidata/{repeat.py → features/repeat.py} +0 -0
  253. {visidata-2.11.1.data → visidata-3.0.1.data}/scripts/vd +0 -0
  254. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/LICENSE.gpl3 +0 -0
  255. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/entry_points.txt +0 -0
  256. {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/top_level.txt +0 -0
visidata/menu.py CHANGED
@@ -1,27 +1,29 @@
1
1
  import string
2
2
  import textwrap
3
+ import curses
3
4
 
4
- from visidata import *
5
-
5
+ from typing import List, Union
6
+ from visidata import vd, drawcache, colors, clipdraw, dispwidth
7
+ from visidata import BaseSheet, VisiData, AttrDict, ENTER
6
8
 
7
9
  vd.option('disp_menu', True, 'show menu on top line when not active', sheettype=None)
8
- vd.option('disp_menu_keys', True, 'show keystrokes inline in submenus', sheettype=None)
9
- vd.option('color_menu', 'black on 110 cyan', 'color of menu items in general')
10
- vd.option('color_menu_active', '223 yellow on black', 'color of active menu items')
11
- vd.option('color_menu_spec', 'black on 34 green', 'color of sheet-specific menu items')
12
- vd.option('color_menu_help', 'black italic on 110 cyan', 'color of helpbox')
13
-
14
- vd.option('disp_menu_boxchars', '││──┌┐└┘├┤', 'box characters to use for menus')
15
- vd.option('disp_menu_more', '»', 'command submenu indicator')
16
- vd.option('disp_menu_push', '⎘', 'indicator if command pushes sheet onto sheet stack')
17
- vd.option('disp_menu_input', '…', 'indicator if input required for command')
18
- vd.option('disp_menu_fmt', 'Ctrl+H for help menu', 'right-side menu format string')
10
+ vd.theme_option('disp_menu_keys', True, 'show keystrokes inline in submenus', sheettype=None)
11
+ vd.theme_option('color_menu', 'black on 68 blue', 'color of menu items in general')
12
+ vd.theme_option('color_menu_active', '223 yellow on black', 'color of active menu items')
13
+ vd.theme_option('color_menu_spec', 'black on 34 green', 'color of sheet-specific menu items')
14
+ vd.theme_option('color_menu_help', 'black italic on 68 blue', 'color of helpbox')
15
+
16
+ vd.theme_option('disp_menu_boxchars', '││──┌┐└┘├┤', 'box characters to use for menus')
17
+ vd.theme_option('disp_menu_more', '»', 'command submenu indicator')
18
+ vd.theme_option('disp_menu_push', '⎘', 'indicator if command pushes sheet onto sheet stack')
19
+ vd.theme_option('disp_menu_input', '…', 'indicator if input required for command')
20
+ vd.option('disp_menu_fmt', '| VisiData {vd.version} | Alt+H for help menu', 'right-side menu format string', max_help=0)
19
21
 
20
22
  BaseSheet.init('activeMenuItems', list)
21
23
  vd.menuRunning = False
22
24
 
23
25
  def menudraw(*args):
24
- return clipdraw(*args, truncator=' ')
26
+ return clipdraw(*args, truncator='')
25
27
 
26
28
 
27
29
  def Menu(title, *args):
@@ -40,7 +42,7 @@ def walkmenu(item, menupath=[]):
40
42
  yield item, menupath
41
43
 
42
44
 
43
- def _finditem(menus, item):
45
+ def _finditem(menus, item:Union[str,int]):
44
46
  if isinstance(item, str):
45
47
  for m in menus:
46
48
  if item == m.title:
@@ -50,12 +52,13 @@ def _finditem(menus, item):
50
52
  return menus[item]
51
53
 
52
54
 
53
- def getMenuItem(obj, menupath=None):
54
- if menupath is None:
55
- menupath = obj.activeMenuItems
55
+ @BaseSheet.api
56
+ def getMenuItem(sheet, menupath:List[str]=None):
57
+ if not menupath:
58
+ menupath = sheet.activeMenuItems
56
59
 
57
60
  try:
58
- currentItem = obj
61
+ currentItem = sheet
59
62
  for i in menupath:
60
63
  currentItem = _finditem(currentItem.menus, i)
61
64
  except IndexError as e:
@@ -74,6 +77,21 @@ def addMenuItem(vd, *args):
74
77
  vd.addMenu(m)
75
78
 
76
79
 
80
+ @VisiData.api
81
+ def addMenuItems(vd, *itemgroups):
82
+ '''Add any number of commands to menu, separated by lines, with individual menupaths separated by '>' character. Example:
83
+ vd.addMenuItems("""
84
+ Help > About > credits > show-credits
85
+ Help > About > environment > show-env
86
+ """)
87
+ '''
88
+ for itemgroup in itemgroups:
89
+ for itemline in itemgroup.splitlines():
90
+ if not itemline: continue
91
+ menupath = [x.strip() for x in itemline.split('>')]
92
+ vd.addMenuItem(*menupath)
93
+
94
+
77
95
  @VisiData.api
78
96
  def addMenu(vd, *args):
79
97
  '''Incorporate submenus and commands into hierarchical menu. Wrap all in Menu() objects. Example:
@@ -92,6 +110,7 @@ def addMenu(vd, *args):
92
110
  c = Menu(p)
93
111
  obj.menus.append(c)
94
112
  obj = c
113
+ assert not obj.menus, 'cannot override submenu with longname'
95
114
  obj.longname = item.longname
96
115
 
97
116
 
@@ -110,370 +129,19 @@ def _intMenuPath(obj, menupath):
110
129
 
111
130
  return [i] + _intMenuPath(obj.menus[i], menupath[1:])
112
131
 
132
+ vd.menus = []
113
133
 
114
- vd.menus = [
115
- Menu('File',
116
- Menu('New', 'open-new'),
117
- Menu('Open file/url', 'open-file'),
118
- Menu('Rename', 'rename-sheet'),
119
- Menu('Guard',
120
- Menu('on', 'guard-sheet'),
121
- Menu('off', 'guard-sheet-off')
122
- ),
123
- Menu('Duplicate',
124
- Menu('selected rows by ref', 'dup-selected'),
125
- Menu('all rows by ref', 'dup-rows'),
126
- Menu('selected rows deep', 'dup-selected-deep'),
127
- Menu('all rows deep', 'dup-rows-deep'),
128
- ),
129
- Menu('Freeze', 'freeze-sheet'),
130
- Menu('Save',
131
- Menu('current sheet', 'save-sheet'),
132
- Menu('all sheets', 'save-all'),
133
- Menu('current column', 'save-col'),
134
- Menu('keys and current column', 'save-col-keys'),
135
- ),
136
- Menu('Commit', 'commit-sheet'),
137
- Menu('Reload', 'reload-sheet'),
138
- Menu('Options',
139
- Menu('all sheets', 'options-global'),
140
- Menu('this sheet', 'options-sheet'),
141
- Menu('edit config file', 'open-config'),
142
- ),
143
- Menu('Quit',
144
- Menu('top sheet', 'quit-sheet'),
145
- Menu('all sheets', 'quit-all'),
146
- ),
147
- ),
148
-
149
- Menu('Edit',
150
- Menu('Undo', 'undo-last'),
151
- Menu('Redo', 'redo-last'),
152
- Menu('Add rows', 'add-rows'),
153
- Menu('Modify',
154
- Menu('current cell',
155
- Menu('input', 'edit-cell'),
156
- Menu('Python expression', 'setcell-expr'),
157
- ),
158
- Menu('selected cells',
159
- Menu('from input', 'setcol-input'),
160
- Menu('increment', 'setcol-incr'),
161
- Menu('Python sequence', 'setcol-expr'),
162
- Menu('regex substitution', 'setcol-subst'),
163
- ),
164
- ),
165
- Menu('Slide',
166
- Menu('Row',
167
- Menu('up', 'slide-up'),
168
- Menu('up N', 'slide-up-n'),
169
- Menu('down', 'slide-down'),
170
- Menu('down N', 'slide-down-n'),
171
- Menu('to top', 'slide-top'),
172
- Menu('to bottom', 'slide-bottom'),
173
- ),
174
- Menu('Column',
175
- Menu('left', 'slide-left'),
176
- Menu('left N', 'slide-left-n'),
177
- Menu('leftmost', 'slide-leftmost'),
178
- Menu('right', 'slide-right'),
179
- Menu('right N', 'slide-right-n'),
180
- Menu('rightmost', 'slide-rightmost'),
181
- ),
182
- ),
183
- Menu('Delete',
184
- Menu('current row', 'delete-row'),
185
- Menu('current cell', 'delete-cell'),
186
- Menu('selected rows', 'delete-selected'),
187
- Menu('selected cells', 'delete-cells'),
188
- Menu('under cursor', 'delete-cursor'),
189
- # Menu('Visible rows ', 'delete-visible'),
190
- ),
191
- Menu('Copy',
192
- Menu('current cell', 'copy-cell'),
193
- Menu('current row', 'copy-row'),
194
- Menu('selected cells', 'copy-cells'),
195
- Menu('selected rows', 'copy-selected'),
196
- Menu('to system clipboard',
197
- Menu('current cell', 'syscopy-cell'),
198
- Menu('current row', 'syscopy-row'),
199
- Menu('selected cells', 'syscopy-cells'),
200
- Menu('selected rows', 'syscopy-selected'),
201
- ),
202
- ),
203
- Menu('Cut',
204
- Menu('current row', 'cut-row'),
205
- Menu('selected cells', 'cut-selected'),
206
- Menu('current cell', 'cut-cell'),
207
- ),
208
- Menu('Paste',
209
- Menu('row after', 'paste-after'),
210
- Menu('row before', 'paste-before'),
211
- Menu('into selected cells', 'setcol-clipboard'),
212
- Menu('into current cell', 'paste-cell'),
213
- Menu('from system clipboard',
214
- Menu('cells at cursor', 'syspaste-cells'),
215
- Menu('selected cells', 'syspaste-cells-selected'),
216
- ),
217
- ),
218
- ),
219
-
220
- Menu('View',
221
- Menu('Statuses', 'open-statuses'),
222
- Menu('Plugins', 'open-plugins'),
223
- Menu('Other sheet',
224
- Menu('previous sheet', 'jump-prev'),
225
- Menu('first sheet', 'jump-first'),
226
- Menu('source sheet', 'jump-source'),
227
- ),
228
- Menu('Sheets',
229
- Menu('stack', 'sheets-stack'),
230
- Menu('all', 'sheets-all'),
231
- ),
232
- Menu('Command log',
233
- Menu('this sheet', 'cmdlog-sheet'),
234
- Menu('this sheet only', 'cmdlog-sheet-only'),
235
- Menu('all commands', 'cmdlog-all'),
236
- ),
237
- Menu('Columns',
238
- Menu('this sheet', 'columns-sheet'),
239
- Menu('all sheets', 'columns-all'),
240
- ),
241
- Menu('Errors',
242
- Menu('recent', 'error-recent'),
243
- Menu('all', 'errors-all'),
244
- Menu('in cell', 'error-cell'),
245
- ),
246
- Menu('Search',
247
- Menu('current column', 'search-col'),
248
- Menu('visible columns', 'search-cols'),
249
- Menu('key columns', 'search-keys'),
250
- Menu('by Python expr', 'search-expr'),
251
- Menu('again', 'search-next'),
252
- ),
253
- Menu('Search backward',
254
- Menu('current column', 'searchr-col'),
255
- Menu('visible columns', 'searchr-cols'),
256
- Menu('key columns', 'searchr-keys'),
257
- Menu('by Python expr', 'searchr-expr'),
258
- Menu('again', 'searchr-next'),
259
- ),
260
- Menu('Split pane',
261
- Menu('in half', 'splitwin-half'),
262
- Menu('in percent', 'splitwin-input'),
263
- Menu('unsplit', 'splitwin-close'),
264
- Menu('swap panes', 'splitwin-swap-pane'),
265
- Menu('goto other pane', 'splitwin-swap'),
266
- ),
267
- Menu('Visibility',
268
- Menu('Methods and dunder attributes',
269
- Menu('show', 'show-hidden'),
270
- Menu('hide', 'hide-hidden'),
271
- ),
272
- ),
273
- Menu('Refresh screen', 'redraw'),
274
- ),
275
- Menu('Column',
276
- Menu('Hide', 'hide-col'),
277
- Menu('Unhide all', 'unhide-cols'),
278
- Menu('Goto',
279
- Menu('by number', 'go-col-number'),
280
- Menu('by name', 'go-col-regex'),
281
- ),
282
- Menu('Resize',
283
- Menu('half', 'resize-col-half'),
284
- Menu('current column to max', 'resize-col-max'),
285
- Menu('current column to input', 'resize-col-input'),
286
- Menu('all columns max', 'resize-cols-max'),
287
- ),
288
- Menu('Rename',
289
- Menu('current column', 'rename-col'),
290
- Menu('from selected cells',
291
- Menu('current column', 'rename-col-selected'),
292
- Menu('unnamed columns', 'rename-cols-row'),
293
- Menu('all columns', 'rename-cols-selected'),
294
- ),
295
- ),
296
- Menu('Type as',
297
- Menu('anytype', 'type-any'),
298
- Menu('string', 'type-string'),
299
- Menu('integer', 'type-int'),
300
- Menu('float', 'type-float'),
301
- Menu('SI float', 'type-floatsi'),
302
- Menu('locale float', 'type-floatlocale'),
303
- Menu('dirty float', 'type-currency'),
304
- Menu('date', 'type-date'),
305
- Menu('custom date format', 'type-customdate'),
306
- Menu('length', 'type-len'),
307
- ),
308
- Menu('Key',
309
- Menu('toggle current column', 'key-col'),
310
- Menu('unkey current column', 'key-col-off'),
311
- ),
312
- Menu('Sort by',
313
- Menu('current column only',
314
- Menu('ascending', 'sort-asc'),
315
- Menu('descending', 'sort-desc'),
316
- ),
317
- Menu('current column too',
318
- Menu('ascending', 'sort-asc-add'),
319
- Menu('descending', 'sort-desc-add'),
320
- ),
321
- Menu('key columns',
322
- Menu('ascending', 'sort-keys-asc'),
323
- Menu('descending', 'sort-keys-desc'),
324
- ),
325
- ),
326
- Menu('Add column',
327
- Menu('empty',
328
- Menu('one column', 'addcol-new'),
329
- Menu('columns', 'addcol-bulk'),
330
- ),
331
- Menu('capture by regex', 'addcol-capture'),
332
- Menu('split by regex', 'addcol-split'),
333
- Menu('subst by regex', 'addcol-subst'),
334
- Menu('Python expr', 'addcol-expr'),
335
- Menu('increment', 'addcol-incr'),
336
- Menu('shell', 'addcol-shell'),
337
- ),
338
- Menu('Expand',
339
- Menu('one level', 'expand-col'),
340
- Menu('to depth', 'expand-col-depth'),
341
- Menu('all columns one level', 'expand-cols'),
342
- Menu('all columns to depth', 'expand-cols-depth'),
343
- ),
344
- Menu('Contract', 'contract-col'),
345
- Menu('Split', 'split-col'),
346
- Menu('Add aggregator', 'aggregate-col'),
347
- Menu('Fill', 'setcol-fill'),
348
- Menu('Freeze', 'freeze-col'),
349
- ),
350
-
351
- Menu('Row',
352
- Menu('Dive into', 'open-row'),
353
- Menu('Goto',
354
- Menu('top', 'go-top'),
355
- Menu('bottom', 'go-bottom'),
356
- Menu('previous',
357
- Menu('page', 'go-pageup'),
358
- Menu('null', 'go-prev-null'),
359
- Menu('value', 'go-prev-value'),
360
- Menu('selected', 'go-prev-selected'),
361
- ),
362
- Menu('next',
363
- Menu('page', 'go-pagedown'),
364
- Menu('null', 'go-next-null'),
365
- Menu('value', 'go-next-value'),
366
- Menu('selected', 'go-next-selected'),
367
- ),
368
- ),
369
- Menu('Select',
370
- Menu('current row', 'select-row'),
371
- Menu('all rows', 'select-rows'),
372
- Menu('from top', 'select-before'),
373
- Menu('to bottom', 'select-after'),
374
- Menu('by Python expr', 'select-expr'),
375
- Menu('by regex',
376
- Menu('current column', 'select-col-regex'),
377
- Menu('all columns', 'select-cols-regex'),
378
- ),
379
- Menu('equal to current cell', 'select-equal-cell'),
380
- Menu('equal to current row', 'select-equal-row'),
381
- Menu('errors',
382
- Menu('current column', 'select-error-col'),
383
- Menu('any column', 'select-error'),
384
- ),
385
- ),
386
- Menu('Unselect',
387
- Menu('current row', 'unselect-row'),
388
- Menu('all rows', 'unselect-rows'),
389
- Menu('from top', 'unselect-before'),
390
- Menu('to bottom', 'unselect-after'),
391
- Menu('by Python expr', 'unselect-expr'),
392
- Menu('by regex',
393
- Menu('current column', 'unselect-col-regex'),
394
- Menu('all columns', 'unselect-cols-regex'),
395
- ),
396
- ),
397
- Menu('Toggle select',
398
- Menu('current row', 'stoggle-row'),
399
- Menu('all rows', 'stoggle-rows'),
400
- Menu('from top', 'stoggle-before'),
401
- Menu('to bottom', 'stoggle-after'),
402
- ),
403
- ),
404
-
405
- Menu('Data',
406
- Menu('Transpose', 'transpose'),
407
- Menu('Frequency table',
408
- Menu('current column', 'freq-col'),
409
- Menu('key columns', 'freq-keys'),
410
- ),
411
- Menu('Statistics', 'describe-sheet'),
412
- Menu('Pivot', 'pivot'),
413
- Menu('Unfurl column', 'unfurl-col'),
414
- Menu('Melt',
415
- Menu('nonkey columns', 'melt'),
416
- Menu('nonkey columns by regex', 'melt-regex'),
417
- ),
418
- Menu('Join',
419
- Menu('top two sheets', 'join-sheets-top2'),
420
- Menu('all sheets', 'join-sheets-all'),
421
- ),
422
- ),
423
-
424
- Menu('Plot',
425
- Menu('Graph',
426
- Menu('current column', 'plot-column'),
427
- Menu('all numeric columns', 'plot-numerics'),
428
- ),
429
- Menu('Resize cursor',
430
- Menu('height',
431
- Menu('double', 'resize-cursor-doubleheight'),
432
- Menu('half','resize-cursor-halfheight'),
433
- Menu('shorter','resize-cursor-shorter'),
434
- Menu('taller','resize-cursor-taller'),
435
- ),
436
- Menu('width',
437
- Menu('double', 'resize-cursor-doublewide'),
438
- Menu('half','resize-cursor-halfwide'),
439
- Menu('thinner','resize-cursor-thinner'),
440
- Menu('wider','resize-cursor-wider'),
441
- ),
442
- ),
443
- Menu('Resize graph',
444
- Menu('X axis', 'resize-x-input'),
445
- Menu('Y axis', 'resize-y-input'),
446
- Menu('aspect ratio', 'set-aspect'),
447
- ),
448
- Menu('Zoom',
449
- Menu('out', 'zoomout-cursor'),
450
- Menu('in', 'zoomin-cursor'),
451
- Menu('cursor', 'zoom-all'),
452
- ),
453
- Menu('Dive into cursor', 'dive-cursor'),
454
- ),
455
- Menu('System',
456
- Menu('Macros sheet', 'macro-sheet'),
457
- Menu('Threads sheet', 'threads-all'),
458
- Menu('Execute longname', 'exec-longname'),
459
- Menu('Python',
460
- Menu('import library', 'import-python'),
461
- Menu('current sheet', 'pyobj-sheet'),
462
- Menu('current row', 'pyobj-row'),
463
- Menu('current cell', 'pyobj-cell'),
464
- Menu('expression', 'pyobj-expr'),
465
- Menu('exec()', 'exec-python'),
466
- ),
467
- Menu('Toggle profiling', 'toggle-profile'),
468
- Menu('Suspend to shell', 'suspend'),
469
- ),
470
- ]
471
-
472
- vd.addMenu(Menu('Help',
473
- Menu('Quick reference', 'sysopen-help'),
474
- Menu('Command list', 'help-commands'),
475
- Menu('Version', 'show-version'),
476
- ))
134
+ vd.addMenu(
135
+ Menu('File'),
136
+ Menu('Edit'),
137
+ Menu('View'),
138
+ Menu('Column'),
139
+ Menu('Row'),
140
+ Menu('Data'),
141
+ Menu('Plot'),
142
+ Menu('System'),
143
+ Menu('Help')
144
+ )
477
145
 
478
146
 
479
147
  @BaseSheet.api
@@ -499,7 +167,7 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
499
167
 
500
168
  maxbinding = 0
501
169
  if vd.options.disp_menu_keys:
502
- maxbinding = max(len(item.binding or '') for item in menus)+2
170
+ maxbinding = max(len(item.binding or '') for item in menus)+1
503
171
 
504
172
  w = max(len(item.title) for item in menus)+maxbinding+2
505
173
 
@@ -512,14 +180,13 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
512
180
  i = 0
513
181
  for j, item in enumerate(menus):
514
182
  attr = colors.color_menu
515
- bindattr = colors.color_keystrokes
516
183
 
517
184
  if any(foo.obj not in ['BaseSheet', 'TableSheet'] for foo, _ in walkmenu(item)):
518
- bindattr = attr = colors.color_menu_spec
185
+ attr = colors.color_menu_spec
519
186
 
520
187
  if level < len(sheet.activeMenuItems):
521
188
  if j == sheet.activeMenuItems[level]:
522
- bindattr = attr = colors.color_menu_active
189
+ attr = colors.color_menu_active
523
190
 
524
191
  if level < len(sheet.activeMenuItems):
525
192
  vd.drawSubmenu(scr, sheet, y+i, x+w+4, item.menus, level+1, disp_menu_boxchars=disp_menu_boxchars)
@@ -532,7 +199,7 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
532
199
  if item.menus:
533
200
  titlenote = vd.options.disp_menu_more
534
201
 
535
- mainbinding = ' '*maxbinding
202
+ mainbinding = ''
536
203
  # special notes
537
204
  if item.cmd:
538
205
  if item.cmd.execstr:
@@ -545,18 +212,17 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
545
212
  revbinds = sheet.revbinds.get(item.cmd.longname, [])
546
213
  if revbinds:
547
214
  mainbinding = vd.prettykeys(revbinds[0])
548
- mainbinding += ' '*(maxbinding-len(mainbinding))
549
215
 
550
216
  # actually display the menu item
551
- title += ' '*(w-len(pretitle)-len(item.title)-maxbinding+1) # padding
217
+ title += ' '*(w-len(pretitle)-len(item.title)+1) # padding
552
218
 
553
219
  menudraw(scr, y+i, x+1, pretitle+title, attr)
554
- if maxbinding:
555
- menudraw(scr, y+i, x+1+w-maxbinding, ' ' + mainbinding, bindattr)
220
+ if maxbinding and mainbinding:
221
+ menudraw(scr, y+i, x+1+w-len(mainbinding), mainbinding, attr.update(colors.keystrokes))
556
222
  menudraw(scr, y+i, x+2+w, titlenote, attr)
557
223
  menudraw(scr, y+i, x+3+w, ls, colors.color_menu)
558
224
 
559
- vd.onMouse(scr, y+i, x, 1, w+3,
225
+ vd.onMouse(scr, x, y+i, w+3, 1,
560
226
  BUTTON1_PRESSED=lambda y,x,key,p=sheet.activeMenuItems[:level]+[j]: sheet.pressMenu(*p),
561
227
  BUTTON2_PRESSED=vd.nop,
562
228
  BUTTON3_PRESSED=vd.nop,
@@ -573,6 +239,11 @@ def nop(vd, *args, **kwargs):
573
239
  return False
574
240
 
575
241
 
242
+ def _done(vd, *args, **kwargs):
243
+ 'Accept and execute current menu item (like pressing Enter).'
244
+ return True
245
+
246
+
576
247
  @BaseSheet.property
577
248
  @drawcache
578
249
  def menus(sheet):
@@ -612,24 +283,22 @@ def menus(sheet):
612
283
  @VisiData.api
613
284
  def drawMenu(vd, scr, sheet):
614
285
  h, w = scr.getmaxyx()
615
- scr.addstr(0, 0, ' '*(w-1), colors.color_menu)
286
+ scr.addstr(0, 0, ' '*(w-1), colors.color_menu.attr)
616
287
  disp_menu_boxchars = sheet.options.disp_menu_boxchars
617
288
  x = 1
618
289
  ymax = 4
619
290
  toplevel = sheet.menus
620
291
  for i, item in enumerate(toplevel):
621
292
  if sheet.activeMenuItems and i == sheet.activeMenuItems[0]:
622
- attr = colors.color_menu_active
293
+ cattr = colors.color_menu_active
623
294
  vd.drawSubmenu(scr, sheet, 1, x, item.menus, 1, disp_menu_boxchars)
624
295
  else:
625
- attr = colors.color_menu
296
+ cattr = colors.color_menu
626
297
 
627
- menudraw(scr, 0, x, ' ', attr)
628
- for j, ch in enumerate(item.title):
629
- menudraw(scr, 0, x+j+1, ch, attr | (curses.A_UNDERLINE if ch.isupper() else 0))
630
- menudraw(scr, 0, x+j+2, ' ', attr)
298
+ menutitle = ' ' + ''.join(f'[:underline]{ch}[/]' if ch.isupper() else ch for ch in item.title) + ' '
299
+ menudraw(scr, 0, x, menutitle, cattr)
631
300
 
632
- vd.onMouse(scr, 0, x, 1, len(item.title)+2,
301
+ vd.onMouse(scr, x, 0, dispwidth(item.title)+2, 1,
633
302
  BUTTON1_PRESSED=lambda y,x,key,i=i,sheet=sheet: sheet.pressMenu(i),
634
303
  BUTTON2_PRESSED=vd.nop,
635
304
  BUTTON3_PRESSED=vd.nop,
@@ -639,14 +308,13 @@ def drawMenu(vd, scr, sheet):
639
308
  BUTTON3_RELEASED=vd.nop)
640
309
  x += len(item.title)+2
641
310
 
642
-
643
311
  rightdisp = sheet.options.disp_menu_fmt.format(sheet=sheet, vd=vd)
644
312
  menudraw(scr, 0, x+4, rightdisp, colors.color_menu)
645
313
 
646
314
  if not sheet.activeMenuItems:
647
315
  return
648
316
 
649
- currentItem = getMenuItem(sheet)
317
+ currentItem = sheet.getMenuItem()
650
318
  cmd = currentItem.cmd
651
319
 
652
320
  if not cmd:
@@ -667,7 +335,7 @@ def drawMenu(vd, scr, sheet):
667
335
 
668
336
  # place helpbox just below deepest menu
669
337
  menuh = 2+sum(sheet.activeMenuItems[1:-1])
670
- menuh += len(getMenuItem(sheet, sheet.activeMenuItems[:-1]).menus)
338
+ menuh += len(sheet.getMenuItem(sheet.activeMenuItems[:-1]).menus)
671
339
  menuy = 16 # min(menuh, h-len(helplines)-3)
672
340
 
673
341
  y = menuy
@@ -695,6 +363,13 @@ def drawMenu(vd, scr, sheet):
695
363
  menudraw(scr, menuy, helpx+2+len(ks)+3, lsr, helpattr)
696
364
  menudraw(scr, menuy, helpx+19, ' '+cmd.longname+' ', helpattr)
697
365
 
366
+ vd.onMouse(scr, helpx, menuy, helpw, y-menuy+1,
367
+ BUTTON1_PRESSED=_done,
368
+ BUTTON1_CLICKED=_done,
369
+ BUTTON1_RELEASED=vd.nop,
370
+ BUTTON2_RELEASED=vd.nop,
371
+ BUTTON3_RELEASED=vd.nop)
372
+
698
373
 
699
374
  @BaseSheet.api
700
375
  def pressMenu(sheet, *args):
@@ -704,7 +379,7 @@ def pressMenu(sheet, *args):
704
379
  ret = False
705
380
  p = _intMenuPath(sheet, args)
706
381
  if p == sheet.activeMenuItems: # clicking on current menu item
707
- if getMenuItem(sheet, p).longname:
382
+ if sheet.getMenuItem(p).longname:
708
383
  ret = True
709
384
  else:
710
385
  p += [0]
@@ -733,6 +408,15 @@ def runMenu(vd):
733
408
  vd.setWindows(vd.scrFull)
734
409
  nEscapes = 0
735
410
 
411
+ def _clickedDuringMenu():
412
+ r = vd.parseMouse(menu=vd.scrMenu, top=vd.winTop, bot=vd.winBottom)
413
+ f = vd.getMouse(r.x, r.y, r.keystroke)
414
+ if f:
415
+ if f(r.y, r.x, r.keystroke): # call each function until one returns a true-ish value
416
+ return 'doit'
417
+ else:
418
+ return 'offmenu'
419
+
736
420
  try:
737
421
  while True:
738
422
  if len(sheet.activeMenuItems) < 2:
@@ -745,7 +429,7 @@ def runMenu(vd):
745
429
  if not k:
746
430
  continue
747
431
 
748
- currentItem = getMenuItem(sheet)
432
+ currentItem = sheet.getMenuItem()
749
433
 
750
434
  if k == '^[': # ESC
751
435
  nEscapes += 1 #1470
@@ -759,14 +443,11 @@ def runMenu(vd):
759
443
  return
760
444
 
761
445
  elif k in ['KEY_MOUSE']:
762
- for keystroke, y, x, winname, winscr in vd.parseMouse(menu=vd.scrMenu, top=vd.winTop, bot=vd.winBottom):
763
- if winname == 'menu':
764
- f = vd.getMouse(winscr, x, y, keystroke)
765
- if f:
766
- if f(y, x, keystroke):
767
- break
768
- else:
769
- return # clicking off the menu is an escape
446
+ r = _clickedDuringMenu()
447
+ if r == 'offmenu':
448
+ return # clicking off the menu is an escape
449
+ elif r == 'doit':
450
+ break
770
451
 
771
452
  elif k in ['KEY_RIGHT', 'l']:
772
453
  if currentItem.menus and sheet.activeMenuItems[1] != 0: # not first item
@@ -810,16 +491,16 @@ def runMenu(vd):
810
491
 
811
492
  main_menu = {'f': 'File', 'e': 'Edit', 'v': 'View', 'c': 'Column', 'r': 'Row', 'd': 'Data', 'p': 'Plot', 's': 'System', 'h': 'Help'}
812
493
 
813
- BaseSheet.addCommand('^[f', 'menu-file', 'pressMenu("File")', '')
814
- BaseSheet.addCommand('^[e', 'menu-edit', 'pressMenu("Edit")', '')
815
- BaseSheet.addCommand('^[v', 'menu-view', 'pressMenu("View")', '')
816
- BaseSheet.addCommand('^[c', 'menu-column', 'pressMenu("Column")', '')
817
- BaseSheet.addCommand('^[r', 'menu-row', 'pressMenu("Row")', '')
818
- BaseSheet.addCommand('^[d', 'menu-data', 'pressMenu("Data")', '')
819
- BaseSheet.addCommand('^[p', 'menu-plot', 'pressMenu("Plot")', '')
820
- BaseSheet.addCommand('^[s', 'menu-system', 'pressMenu("System")', '')
821
- BaseSheet.addCommand('^[h', 'menu-help', 'pressMenu("Help")', '')
822
- BaseSheet.bindkey('^H', 'menu-help')
494
+ BaseSheet.addCommand('Alt+f', 'menu-file', 'pressMenu("File")', '')
495
+ BaseSheet.addCommand('Alt+e', 'menu-edit', 'pressMenu("Edit")', '')
496
+ BaseSheet.addCommand('Alt+v', 'menu-view', 'pressMenu("View")', '')
497
+ BaseSheet.addCommand('Alt+c', 'menu-column', 'pressMenu("Column")', '')
498
+ BaseSheet.addCommand('Alt+r', 'menu-row', 'pressMenu("Row")', '')
499
+ BaseSheet.addCommand('Alt+d', 'menu-data', 'pressMenu("Data")', '')
500
+ BaseSheet.addCommand('Alt+p', 'menu-plot', 'pressMenu("Plot")', '')
501
+ BaseSheet.addCommand('Alt+s', 'menu-system', 'pressMenu("System")', '')
502
+ BaseSheet.addCommand('Alt+h', 'menu-help', 'pressMenu("Help")', '')
503
+ BaseSheet.bindkey('Ctrl+H', 'menu-help')
823
504
  BaseSheet.bindkey('KEY_BACKSPACE', 'menu-help')
824
505
 
825
506
  vd.addGlobals({'Menu': Menu})