visidata 2.11.1__py3-none-any.whl → 3.0__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 (255) 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 +65 -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 +59 -50
  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 +5 -1
  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 +30 -5
  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 +163 -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} +75 -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 +180 -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 +17 -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 +48 -10
  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 +301 -148
  187. visidata/man/vd.txt +290 -153
  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 +50 -201
  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 +229 -257
  206. visidata/shell.py +51 -21
  207. visidata/sidebar.py +162 -0
  208. visidata/sort.py +11 -4
  209. visidata/statusbar.py +113 -104
  210. visidata/stored_list.py +43 -0
  211. visidata/stored_prop.py +38 -0
  212. visidata/tests/conftest.py +3 -3
  213. visidata/tests/test_cliptext.py +39 -0
  214. visidata/tests/test_commands.py +62 -7
  215. visidata/tests/test_edittext.py +2 -2
  216. visidata/tests/test_features.py +17 -0
  217. visidata/tests/test_menu.py +14 -0
  218. visidata/tests/test_path.py +13 -4
  219. visidata/text_source.py +53 -0
  220. visidata/textsheet.py +10 -3
  221. visidata/theme.py +44 -0
  222. visidata/themes/__init__.py +0 -0
  223. visidata/themes/ascii8.py +84 -0
  224. visidata/themes/asciimono.py +84 -0
  225. visidata/themes/light.py +17 -0
  226. visidata/threads.py +87 -39
  227. visidata/tuiwin.py +22 -0
  228. visidata/type_currency.py +22 -3
  229. visidata/type_date.py +31 -9
  230. visidata/type_floatsi.py +5 -1
  231. visidata/undo.py +17 -5
  232. visidata/utils.py +106 -23
  233. visidata/vdobj.py +28 -17
  234. visidata/windows.py +10 -0
  235. visidata/wrappers.py +9 -3
  236. visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
  237. {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/vd.1 +301 -148
  238. {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +301 -148
  239. visidata-3.0.data/scripts/vd2to3.vdx +9 -0
  240. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/METADATA +12 -8
  241. visidata-3.0.dist-info/RECORD +257 -0
  242. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
  243. vgit/__init__.py +0 -1
  244. vgit/gitsheet.py +0 -164
  245. visidata/layout.py +0 -44
  246. visidata/misc.py +0 -5
  247. visidata-2.11.1.data/scripts/vgit +0 -9
  248. visidata-2.11.1.dist-info/RECORD +0 -155
  249. {vgit → visidata/apps/vgit}/__main__.py +0 -0
  250. {vgit → visidata/apps/vgit}/abort.py +0 -0
  251. /visidata/{repeat.py → features/repeat.py} +0 -0
  252. {visidata-2.11.1.data → visidata-3.0.data}/scripts/vd +0 -0
  253. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
  254. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -0
  255. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/guide.py ADDED
@@ -0,0 +1,180 @@
1
+ '''
2
+ # A Guide to VisiData Guides
3
+ Each guide shows you how to use a particular feature in VisiData. Gray guides have not been written yet. We love contributions: [:onclick https://visidata.org/docs/api/guides]https://visidata.org/docs/api/guides[/].
4
+
5
+ - [:keystrokes]Up/Down[/] to move the row cursor
6
+ - [:keystrokes]Enter[/] to view a topic
7
+ '''
8
+ import re
9
+
10
+ from visidata import vd, BaseSheet, Sheet, ItemColumn, Column, VisiData, ENTER, RowColorizer, AttrDict, MissingAttrFormatter
11
+ from visidata import wraptext
12
+
13
+ guides_list = '''
14
+ GuideIndex ("A Guide to VisiData Guides (you are here)")
15
+ HelpGuide ("Where to Start and How to Quit") # manpage; ask for patreon
16
+ MenuGuide ("The VisiData Menu System")
17
+ CommandsSheet ("How to find the command you want run")
18
+
19
+ # real barebones basics
20
+ MovementGuide ("Movement and Search")
21
+ SortGuide ("Sorting")
22
+ TypesSheet ("The basic type system")
23
+ CommandLog ("Undo and Replay")
24
+
25
+ # rev this thing up
26
+
27
+ SelectionGuide ("Selecting and filtering") # stu|, and variants; filtering with dup-sheet; g prefix often refers to selected rowset
28
+ SheetsSheet ("The Sheet Stack")
29
+ ColumnsSheet ("Columns: the only way to fly")
30
+ StatusesSheet ("Revisit old status messages")
31
+ SidebarSheet ("Dive into the sidebar")
32
+ SaversGuide ("Saving Data") # talk about options.overwrite + ro here
33
+
34
+ ErrorsSheet ("What was that error?")
35
+ ModifyGuide ("Adding, Editing, Deleting Rows")
36
+
37
+ # the varieties of data experience
38
+
39
+ SlideGuide ("Sliding rows and columns around")
40
+ ExprGuide ("Compute Python over every row")
41
+ JoinGuide ("Joining multiple sheets together")
42
+ DescribeSheet ("Basic Statistics (min/max/mode/median/mean)")
43
+ AggregatorsSheet ("Aggregations like sum, mean, and distinct")
44
+ FrequencyTable ("Frequency Tables are how you GROUP BY")
45
+ PivotGuide ("Pivot Tables are just Frequency Tables with more columns")
46
+ MeltGuide ("Melt is just Unpivot")
47
+ JsonGuide ("Some special features for JSON") # with expand/contract, unfurl
48
+ RegexGuide ("Matching and Transforming Strings with Regex")
49
+ GraphSheet ("Basic scatterplots and other graphs")
50
+ WindowFunctionGuide ("Perform operations on groups of rows")
51
+
52
+ # for the frequent user
53
+ OptionsSheet ("Options and Settings")
54
+ ClipboardGuide ("Copy and Paste Data via the Clipboard")
55
+ DirSheet ("Browsing the local filesystem")
56
+ FormatsSheet ("What can you open with VisiData?")
57
+ SplitpaneGuide ("Split VisiData into two panes")
58
+ ThemesSheet ("Change Interface Theme")
59
+ ColorSheet ("See available colors")
60
+ MacrosSheet ("Recording macros")
61
+ MemorySheet ("Making note of certain values")
62
+
63
+ # advanced usage and developers
64
+
65
+ ThreadsSheet ("Threads past and present")
66
+ PyobjSheet ("Inspecting internal Python objects")
67
+
68
+ # appendices
69
+
70
+ InputEditorGuide ("Using the builtin line editor")
71
+ '''
72
+
73
+ vd.guides = {} # name -> guidecls
74
+
75
+ @VisiData.api
76
+ def addGuide(vd, name, guidecls):
77
+ vd.guides[name] = guidecls
78
+
79
+ @VisiData.api
80
+ class GuideIndex(Sheet):
81
+ guide = __doc__
82
+
83
+ rowtype = 'guides' # rowdef: list(guide number, guide name, topic description, points, max_points)
84
+ columns = [
85
+ ItemColumn('n', 0, type=int),
86
+ ItemColumn('name', 1, width=0),
87
+ ItemColumn('topic', 2, width=60),
88
+ ]
89
+ colorizers = [
90
+ RowColorizer(7, 'color_guide_unwritten', lambda s,c,r,v: r and r[1] not in vd.guides)
91
+ ]
92
+ def iterload(self):
93
+ i = 0
94
+ for line in guides_list.splitlines():
95
+ m = re.search(r'(\w+?) \("(.*)"\)', line)
96
+ if m:
97
+ yield [i] + list(m.groups())
98
+ i += 1
99
+
100
+ def openRow(self, row):
101
+ name = row[1]
102
+ return vd.getGuide(name)
103
+
104
+ class OptionHelpGetter:
105
+ 'For easy and consistent formatting in sidebars and helpstrings, use {vd.options.help.opt_name}.'
106
+ def __getattr__(self, optname):
107
+ opt = vd.options._get(optname, 'default')
108
+ return f'[:onclick options-sheet {optname}]`{optname}`[/]: to {opt.helpstr} (default: {opt.value})'
109
+
110
+
111
+ class CommandHelpGetter:
112
+ 'For easy and consistent formatting in sidebars and helpstrings, use {vd.commands.help.long_name}.'
113
+ def __init__(self, cls):
114
+ self.cls = cls
115
+ self.helpsheet = vd.HelpSheet()
116
+ self.helpsheet.ensureLoaded()
117
+
118
+ def __getattr__(self, k):
119
+ longname = k.replace('_', '-')
120
+ binding = self.helpsheet.revbinds.get(longname, [None])[0]
121
+ # cmddict has a SheetClass associated with each command
122
+ # go through all the parents of the Sheet type, to look for the command
123
+ for cls in self.cls.superclasses():
124
+ cmd = self.helpsheet.cmddict.get((cls.__name__, longname), None)
125
+ if cmd:
126
+ break
127
+ if not cmd:
128
+ return ''
129
+ if 'input' in cmd.execstr.lower():
130
+ inputtype = 'input'
131
+ m = re.search(r'type="(\w*)"', cmd.execstr, re.IGNORECASE)
132
+ if not m:
133
+ m = re.search(r'input(\w*)\("', cmd.execstr, re.IGNORECASE)
134
+ if m:
135
+ inputtype = m.groups()[0].lower()
136
+ binding += f'[:45] {inputtype}[/]'
137
+
138
+ helpstr = cmd.helpstr
139
+ return f'`{binding}` (`{longname}`) to {helpstr}'
140
+
141
+
142
+ class GuideSheet(Sheet):
143
+ rowtype = 'lines'
144
+ filetype = 'guide'
145
+ columns = [
146
+ ItemColumn('linenum', 0, type=int, width=0),
147
+ ItemColumn('guide', 1, width=80, displayer='full'),
148
+ ]
149
+ precious = False
150
+ guide_text = ''
151
+ sheettype = Sheet
152
+
153
+ def iterload(self):
154
+ winWidth = 78
155
+ helper = AttrDict(commands=CommandHelpGetter(self.sheettype),
156
+ options=OptionHelpGetter())
157
+ guidetext = MissingAttrFormatter().format(self.guide_text, help=helper, vd=vd)
158
+ for startingLine, text in enumerate(guidetext.splitlines()):
159
+ text = text.strip()
160
+ if text:
161
+ for i, (L, _) in enumerate(wraptext(str(text), width=winWidth)):
162
+ yield [startingLine+i+1, L]
163
+ else:
164
+ yield [startingLine+1, text]
165
+
166
+
167
+
168
+ @VisiData.api
169
+ def getGuide(vd, name): # -> GuideSheet()
170
+ if name in vd.guides:
171
+ return vd.guides[name]()
172
+ vd.warning(f'no guide named {name}')
173
+
174
+ BaseSheet.addCommand('', 'open-guide-index', 'vd.push(GuideIndex("VisiData_Guide"))', 'open VisiData guides table of contents')
175
+
176
+ vd.addMenuItems('''
177
+ Help > VisiData Feature Guides > open-guide-index
178
+ ''')
179
+
180
+ vd.addGlobals({'GuideSheet':GuideSheet, "CommandHelpGetter": CommandHelpGetter, "OptionHelpGetter": OptionHelpGetter})
visidata/help.py CHANGED
@@ -1,10 +1,35 @@
1
1
  import functools
2
2
  import collections
3
- from pkg_resources import resource_filename
4
3
 
5
- from visidata import *
4
+ from visidata import VisiData, MetaSheet, ColumnAttr, Column, BaseSheet, VisiDataMetaSheet, SuspendCurses
5
+ from visidata import vd, asyncthread, ENTER, drawcache, AttrDict
6
6
 
7
- vd.show_help = False
7
+ vd.option('disp_help', 2, 'show help panel during input')
8
+
9
+ @BaseSheet.api
10
+ def hint_basichelp(sheet):
11
+ return 0, '`Alt+[:underline]H[/]` to open the [:underline]H[/]elp menu'
12
+
13
+
14
+ @VisiData.api
15
+ def iterMenuPaths(vd, item=None, menupath=[]):
16
+ 'Generate (longname, menupath).'
17
+ if item is None:
18
+ item = vd.menus
19
+
20
+ if isinstance(item, (list, tuple)):
21
+ for m in item:
22
+ yield from vd.iterMenuPaths(m, menupath)
23
+ elif item.longname:
24
+ yield item.longname, ' > '.join(menupath+[item.title])
25
+ else:
26
+ yield from vd.iterMenuPaths(item.menus, menupath+[item.title])
27
+
28
+
29
+ @VisiData.property
30
+ @drawcache
31
+ def menuPathsByLongname(vd):
32
+ return dict(vd.iterMenuPaths())
8
33
 
9
34
 
10
35
  @VisiData.api
@@ -16,10 +41,12 @@ class HelpSheet(MetaSheet):
16
41
 
17
42
  columns = [
18
43
  ColumnAttr('sheet'),
44
+ ColumnAttr('module'),
19
45
  ColumnAttr('longname'),
46
+ Column('menupath', width=0, cache=True, getter=lambda col,row: vd.menuPathsByLongname[row.longname]),
20
47
  Column('keystrokes', getter=lambda col,row: col.sheet.revbinds.get(row.longname, [None])[0]),
21
- Column('all_bindings', width=0, getter=lambda col,row: list(set(col.sheet.revbinds.get(row.longname, [])))),
22
- Column('description', getter=lambda col,row: col.sheet.cmddict[(row.sheet, row.longname)].helpstr),
48
+ Column('all_bindings', width=0, cache=True, getter=lambda col,row: list(set(col.sheet.revbinds.get(row.longname, [])))),
49
+ Column('description', width=40, getter=lambda col,row: col.sheet.cmddict[(row.sheet, row.longname)].helpstr),
23
50
  ColumnAttr('execstr', width=0),
24
51
  Column('logged', width=0, getter=lambda col,row: vd.isLoggableCommand(row.longname)),
25
52
  ]
@@ -54,73 +81,69 @@ class HelpSheet(MetaSheet):
54
81
  return revbinds
55
82
 
56
83
 
57
- @VisiData.api
58
- @asyncthread
59
- def help_search(vd, sheet, regex):
60
- vs = HelpSheet(source=None)
61
- vs.rows = [] # do not trigger push reload
62
- vd.push(vs) # push first, then reload
63
- vd.sync(vs.reload())
64
-
65
- # find rows matching regex on original HelpSheet
66
- rowidxs = list(vd.searchRegex(vs, regex=regex, columns="visibleCols"))
67
-
68
- # add only matching rows
69
- allrows = vs.rows
70
- vs.rows = []
71
- for rowidx in rowidxs:
72
- vs.addRow(allrows[rowidx])
73
-
74
-
75
84
  class HelpPane:
76
85
  def __init__(self, name):
86
+ import visidata
77
87
  self.name = name
78
88
  self.scr = None
79
89
  self.parentscr = None
80
90
  self.amgr = visidata.AnimationMgr()
81
91
 
82
- def draw(self, scr, x=None, y=None):
92
+ @property
93
+ def width(self):
94
+ return self.amgr.maxWidth
95
+
96
+ @property
97
+ def height(self):
98
+ return self.amgr.maxHeight
99
+
100
+ def draw(self, scr, x=None, y=None, **kwargs):
83
101
  if not scr: return
84
- if not vd.show_help:
85
- if self.scr:
86
- self.scr.erase()
87
- self.scr.refresh()
88
- self.scr = None
89
- return
102
+ # if vd.options.disp_help <= 0:
103
+ # if self.scr:
104
+ # self.scr.erase()
105
+ # self.scr.refresh()
106
+ # self.scr = None
107
+ # return
90
108
  if y is None: y=0 # show at top of screen by default
91
109
  if x is None: x=0
110
+ hneeded = self.amgr.maxHeight+3
111
+ wneeded = self.amgr.maxWidth+4
112
+ scrh, scrw = scr.getmaxyx()
92
113
  if not self.scr or scr is not self.parentscr: # (re)allocate help pane scr
93
114
  if y >= 0:
94
- if y+self.amgr.maxHeight+3 < scr.getmaxyx()[0]:
115
+ if y+hneeded < scrh:
95
116
  yhelp = y+1
96
117
  else:
97
- yhelp = y-self.amgr.maxHeight-3
118
+ hneeded = max(0, min(hneeded, y-1))
119
+ yhelp = y-hneeded
98
120
  else: # y<0
99
- yhelp = scr.getmaxyx()[0]-self.amgr.maxHeight-4
121
+ yhelp = max(0, scrh-hneeded-1)
100
122
 
101
123
  if x >= 0:
102
- if x+self.amgr.maxWidth+4 < scr.getmaxyx()[1]:
124
+ if x+wneeded < scrw:
103
125
  xhelp = x+1
104
126
  else:
105
- xhelp = x-self.amgr.maxWidth-4
127
+ wneeded = max(0, min(wneeded, x-1))
128
+ xhelp = x-wneeded
106
129
  else: # x<0
107
- xhelp = scr.getmaxyx()[1]-self.amgr.maxWidth-5
130
+ xhelp = max(0, scrh-wneeded-1)
108
131
 
109
- self.scr = scr.derwin(self.amgr.maxHeight+3, self.amgr.maxWidth+4, yhelp, xhelp)
132
+ self.scr = vd.subwindow(scr, xhelp, yhelp, wneeded, hneeded)
110
133
  self.parentscr = scr
111
134
 
112
135
  self.scr.erase()
113
136
  self.scr.box()
114
- self.amgr.draw(self.scr, y=1, x=2)
137
+ self.amgr.draw(self.scr, y=1, x=2, **kwargs)
115
138
  self.scr.refresh()
116
139
 
117
140
 
118
141
  @VisiData.api
119
142
  @functools.lru_cache(maxsize=None)
120
- def getHelpPane(vd, name, module='vdplus'):
143
+ def getHelpPane(vd, name, module='visidata') -> HelpPane:
121
144
  ret = HelpPane(name)
122
145
  try:
123
- ret.amgr.load(name, Path(resource_filename(module,'ddw/'+name+'.ddw')).open_text(encoding='utf-8'))
146
+ ret.amgr.load(name, (vd.pkg_resources_files(module)/f'ddw/{name}.ddw').open(encoding='utf-8'))
124
147
  ret.amgr.trigger(name, loop=True)
125
148
  except FileNotFoundError as e:
126
149
  vd.debug(str(e))
@@ -135,23 +158,31 @@ def getHelpPane(vd, name, module='vdplus'):
135
158
  def openManPage(vd):
136
159
  import os
137
160
  with SuspendCurses():
138
- if os.system(' '.join(['man', resource_filename(__name__, 'man/vd.1')])) != 0:
139
- vd.push(TextSheet('man_vd', source=Path(resource_filename(__name__, 'man/vd.txt'))))
161
+ module_path = vd.pkg_resources_files(__name__.split('.')[0])
162
+ if os.system(' '.join(['man', str(module_path/'man/vd.1')])) != 0:
163
+ vd.push(TextSheet('man_vd', source=module_path/'man/vd.txt'))
140
164
 
141
165
 
142
166
  # in VisiData, g^H refers to the man page
143
167
  BaseSheet.addCommand('g^H', 'sysopen-help', 'openManPage()', 'Show the UNIX man page for VisiData')
144
168
  BaseSheet.addCommand('z^H', 'help-commands', 'vd.push(HelpSheet(name + "_commands", source=sheet, revbinds={}))', 'list commands and keybindings available on current sheet')
145
169
  BaseSheet.addCommand('gz^H', 'help-commands-all', 'vd.push(HelpSheet("all_commands", source=None, revbinds={}))', 'list commands and keybindings for all sheet types')
146
- BaseSheet.addCommand(None, 'help-search', 'help_search(sheet, input("help: "))', 'search through command longnames with search terms')
147
170
 
148
171
  BaseSheet.bindkey('KEY_F(1)', 'sysopen-help')
149
172
  BaseSheet.bindkey('zKEY_F(1)', 'help-commands')
150
173
  BaseSheet.bindkey('zKEY_BACKSPACE', 'help-commands')
174
+ BaseSheet.bindkey('gKEY_BACKSPACE', 'sysopen-help')
151
175
 
152
- HelpSheet.addCommand(ENTER, 'exec-command', 'quit(sheet); draw_all(); activeStack[0].execCommand(cursorRow.longname)', 'execute command on undersheet')
176
+ HelpSheet.addCommand(None, 'exec-command', 'quit(sheet); draw_all(); activeStack[0].execCommand(cursorRow.longname)', 'execute command on undersheet')
153
177
  BaseSheet.addCommand(None, 'open-tutorial-visidata', 'launchBrowser("https://jsvine.github.io/intro-to-visidata/")', 'open https://jsvine.github.io/intro-to-visidata/')
154
178
 
155
179
  vd.addMenuItem("Help", "VisiData tutorial", 'open-tutorial-visidata')
156
180
  vd.addMenuItem("Help", 'Sheet commands', 'help-commands')
157
181
  vd.addMenuItem("Help", 'All commands', 'help-commands-all')
182
+
183
+ vd.addGlobals(HelpSheet=HelpSheet)
184
+
185
+ vd.addMenuItems('''
186
+ Help > Quick reference > sysopen-help
187
+ Help > Command list > help-commands
188
+ ''')
visidata/hint.py ADDED
@@ -0,0 +1,39 @@
1
+ import collections
2
+
3
+ from visidata import vd, BaseSheet
4
+
5
+
6
+ @BaseSheet.lazy_property
7
+ def prevHints(sheet):
8
+ return collections.defaultdict(int)
9
+
10
+
11
+ @BaseSheet.api
12
+ def getHint(sheet, *args, **kwargs) -> str:
13
+ funcs = [getattr(sheet, x) for x in dir(sheet) if x.startswith('hint_')]
14
+ results = []
15
+ hints = sheet.prevHints
16
+ for f in funcs:
17
+ try:
18
+ r = f(*args, **kwargs)
19
+ if r:
20
+ if isinstance(r, dict):
21
+ n = r.get('_relevance', 1)
22
+ v = r
23
+ elif isinstance(r, tuple):
24
+ n, v = r
25
+ else:
26
+ n = 1
27
+ v = r
28
+
29
+ if v not in sheet.prevHints:
30
+ results.append((n, v))
31
+ sheet.prevHints[v] += 1
32
+ except Exception as e:
33
+ vd.debug(f'{f.__name__}: {e}')
34
+
35
+ if results:
36
+ return sorted(results, reverse=True)[0][1]
37
+
38
+
39
+ vd.addCommand('', 'help-hint', 'status(getHint() or pressMenu("Help"))', 'get context-dependent hint on what to do next')
visidata/indexsheet.py ADDED
@@ -0,0 +1,109 @@
1
+ from visidata import vd, VisiData, BaseSheet, Sheet, Column, AttrColumn, ItemColumn, setitem, asyncthread
2
+
3
+
4
+ class IndexSheet(Sheet):
5
+ 'Base class for tabular sheets with rows that are Sheets.'
6
+ guide = '''
7
+ # Index Sheet
8
+ This is a list of sheets from `{sheet.source}`.
9
+
10
+ - `Enter` to open {sheet.cursorRow}
11
+ - `g Enter` to open all selected sheets
12
+ '''
13
+ rowtype = 'sheets' # rowdef: Sheet
14
+
15
+ columns = [
16
+ Column('name', getter=lambda c,r: r.names[-1], setter=lambda c,r,v: setitem(r.names, -1, v)),
17
+ AttrColumn('rows', 'nRows', type=int, width=9),
18
+ AttrColumn('cols', 'nCols', type=int),
19
+ AttrColumn('keys', 'keyColNames'),
20
+ AttrColumn('source'),
21
+ ]
22
+ nKeys = 1
23
+
24
+ def newRow(self):
25
+ return Sheet('', columns=[ItemColumn('', 0)], rows=[])
26
+
27
+ def openRow(self, row):
28
+ return row # rowdef is Sheet
29
+
30
+ def getSheet(self, k):
31
+ for vs in self.rows:
32
+ if vs.name == k:
33
+ return vs
34
+
35
+ def addRow(self, sheet, **kwargs):
36
+ super().addRow(sheet, **kwargs)
37
+ if not self.options.load_lazy and not sheet.options.load_lazy:
38
+ sheet.ensureLoaded()
39
+
40
+ @asyncthread
41
+ def reloadSheets(self, sheets):
42
+ for vs in vd.Progress(sheets):
43
+ vs.reload()
44
+
45
+
46
+ class SheetsSheet(IndexSheet):
47
+ columns = [
48
+ AttrColumn('name'),
49
+ AttrColumn('type', '__class__.__name__'),
50
+ AttrColumn('pane', type=int),
51
+ Column('shortcut', getter=lambda c,r: getattr(r, 'shortcut'), setter=lambda c,r,v: setattr(r, '_shortcut', v)),
52
+ AttrColumn('nRows', type=int),
53
+ AttrColumn('nCols', type=int),
54
+ AttrColumn('nVisibleCols', type=int),
55
+ AttrColumn('cursorDisplay'),
56
+ AttrColumn('keyColNames'),
57
+ AttrColumn('source'),
58
+ AttrColumn('progressPct'),
59
+ # AttrColumn('threads', 'currentThreads', type=vlen),
60
+ ]
61
+ precious = False
62
+ nKeys = 1
63
+ def reload(self):
64
+ self.rows = self.source
65
+
66
+ def sort(self):
67
+ self.rows[1:] = sorted(self.rows[1:], key=self.sortkey)
68
+
69
+
70
+ class GlobalSheetsSheet(SheetsSheet): #1620
71
+ def sort(self):
72
+ IndexSheet.sort(self)
73
+
74
+
75
+ @VisiData.lazy_property
76
+ def sheetsSheet(vd):
77
+ return SheetsSheet("sheets", source=vd.sheets)
78
+
79
+
80
+ @VisiData.lazy_property
81
+ def allSheetsSheet(vd):
82
+ return GlobalSheetsSheet("sheets_all", source=vd.allSheets)
83
+
84
+
85
+
86
+ @Sheet.api
87
+ def nextRow(sheet, n=1):
88
+ sheet.cursorRowIndex += n
89
+ sheet.checkCursor()
90
+ return sheet.rows[sheet.cursorRowIndex] # cursorRow itself might be cached
91
+
92
+
93
+ vd.addCommand('S', 'sheets-stack', 'vd.push(vd.sheetsSheet)', 'open Sheets Stack: join or jump between the active sheets on the current stack')
94
+ vd.addCommand('gS', 'sheets-all', 'vd.push(vd.allSheetsSheet)', 'open Sheets Sheet: join or jump between all sheets from current session')
95
+
96
+ BaseSheet.addCommand('g>', 'open-source-next', 'vd.replace(openSource(source.nextRow())) if isinstance(source, IndexSheet) else fail("parent sheet must be Index Sheet")', 'open next sheet on parent index sheet')
97
+ BaseSheet.addCommand('g<', 'open-source-prev', 'vd.replace(openSource(source.nextRow(-1))) if isinstance(source, IndexSheet) else fail("parent sheet must be Index Sheet")', 'open prev sheet on parent index sheet')
98
+
99
+ IndexSheet.addCommand('g^R', 'reload-selected', 'reloadSheets(selectedRows or rows)', 'reload all selected sheets')
100
+
101
+ # when diving into a sheet, remove the index unless it is precious
102
+ SheetsSheet.addCommand('gC', 'columns-selected', 'vd.push(ColumnsSheet("all_columns", source=selectedRows))', 'open Columns Sheet with all visible columns from selected sheets')
103
+ SheetsSheet.addCommand('z^C', 'cancel-row', 'cancelThread(*cursorRow.currentThreads)', 'abort async thread for current sheet')
104
+ SheetsSheet.addCommand('gz^C', 'cancel-rows', 'for vs in selectedRows: cancelThread(*vs.currentThreads)', 'abort async threads for selected sheets')
105
+ SheetsSheet.addCommand('Enter', 'open-row', 'dest=cursorRow; vd.sheets.remove(sheet) if not sheet.precious else None; vd.push(openRow(dest))', 'open sheet referenced in current row')
106
+
107
+ vd.addGlobals(IndexSheet=IndexSheet,
108
+ SheetsSheet=SheetsSheet,
109
+ GlobalSheetsSheet=GlobalSheetsSheet)
@@ -0,0 +1,55 @@
1
+ from visidata import vd, VisiData, Sheet, ItemColumn, asyncthread
2
+
3
+
4
+ vd._inputHistoryList = vd.StoredList(name='input_history')
5
+ vd.inputHistory = {} # [input_type][input] -> anything
6
+
7
+
8
+ @VisiData.api
9
+ def addInputHistory(vd, input:str, type:str=''):
10
+ r = vd.processInputHistory(input, type)
11
+ if r:
12
+ vd._inputHistoryList.append(r)
13
+
14
+
15
+ @VisiData.api
16
+ def processInputHistory(vd, input:str, type:str=''):
17
+ hist = list(vd.inputHistory.setdefault(type, {}).keys())
18
+ if hist and hist[-1] == input:
19
+ return
20
+ if input in vd.inputHistory[type]:
21
+ n = vd.inputHistory[type][input].get('n', 0)
22
+ del vd.inputHistory[type][input] # make it the most recent entry
23
+ else:
24
+ n = 0
25
+
26
+ r = dict(type=type, input=input, n=n+1)
27
+ vd.inputHistory[type][input] = r
28
+ return r
29
+
30
+
31
+ class InputHistorySheet(Sheet):
32
+ # rowdef: dict(type=, input=, n=)
33
+ # .source=vd.inputHistory
34
+ columns = [
35
+ ItemColumn('type'),
36
+ ItemColumn('input'),
37
+ ]
38
+ def iterload(self):
39
+ yield from vd._inputHistoryList
40
+
41
+
42
+ @VisiData.before
43
+ @asyncthread
44
+ def run(vd, *args, **kwargs):
45
+ vd._inputHistoryList.reload()
46
+ for x in vd._inputHistoryList:
47
+ vd.processInputHistory(x.input, x.type)
48
+
49
+
50
+ @VisiData.property
51
+ def inputHistorySheet(vd):
52
+ return InputHistorySheet('input_history', source=vd._inputHistoryList.path)
53
+
54
+
55
+ vd.addCommand(None, 'open-input-history', 'vd.push(inputHistorySheet)', 'open sheet with previous inputs')
visidata/interface.py ADDED
@@ -0,0 +1,58 @@
1
+ from visidata import VisiData, vd
2
+
3
+ vd.theme_option('disp_splitwin_pct', 0, 'height of second sheet on screen')
4
+ vd.theme_option('disp_note_none', '⌀', 'visible contents of a cell whose value is None')
5
+ vd.theme_option('disp_truncator', '…', 'indicator that the contents are only partially visible')
6
+ vd.theme_option('disp_oddspace', '\u00b7', 'displayable character for odd whitespace')
7
+ vd.theme_option('disp_more_left', '<', 'header note indicating more columns to the left')
8
+ vd.theme_option('disp_more_right', '>', 'header note indicating more columns to the right')
9
+ vd.theme_option('disp_error_val', '', 'displayed contents for computation exception')
10
+ vd.theme_option('disp_ambig_width', 1, 'width to use for unicode chars marked ambiguous')
11
+
12
+ vd.theme_option('disp_pending', '', 'string to display in pending cells')
13
+ vd.theme_option('note_pending', '⌛', 'note to display for pending cells')
14
+ vd.theme_option('note_format_exc', '?', 'cell note for an exception during formatting')
15
+ vd.theme_option('note_getter_exc', '!', 'cell note for an exception during computation')
16
+ vd.theme_option('note_type_exc', '!', 'cell note for an exception during type conversion')
17
+
18
+ vd.theme_option('color_note_pending', 'bold magenta', 'color of note in pending cells')
19
+ vd.theme_option('color_note_type', '226 yellow', 'color of cell note for non-str types in anytype columns')
20
+ vd.theme_option('color_note_row', '220 yellow', 'color of row note on left edge')
21
+ vd.option('scroll_incr', -3, 'amount to scroll with scrollwheel')
22
+ vd.theme_option('disp_column_sep', '│', 'separator between columns')
23
+ vd.theme_option('disp_keycol_sep', '║', 'separator between key columns and rest of columns')
24
+ vd.theme_option('disp_rowtop_sep', '│', '') # ╷│┬╽⌜⌐▇
25
+ vd.theme_option('disp_rowmid_sep', '⁝', '') # ┃┊│█
26
+ vd.theme_option('disp_rowbot_sep', '⁝', '') # ┊┴╿⌞█⍿╵⎢┴⌊ ⋮⁝
27
+ vd.theme_option('disp_rowend_sep', '║', '') # ┊┴╿⌞█⍿╵⎢┴⌊
28
+ vd.theme_option('disp_keytop_sep', '║', '') # ╽╿┃╖╟
29
+ vd.theme_option('disp_keymid_sep', '║', '') # ╽╿┃
30
+ vd.theme_option('disp_keybot_sep', '║', '') # ╽╿┃╜‖
31
+ vd.theme_option('disp_endtop_sep', '║', '') # ╽╿┃╖╢
32
+ vd.theme_option('disp_endmid_sep', '║', '') # ╽╿┃
33
+ vd.theme_option('disp_endbot_sep', '║', '') # ╽╿┃╜‖
34
+ vd.theme_option('disp_selected_note', '•', '') #
35
+ vd.theme_option('disp_sort_asc', '↑↟⇞⇡⇧⇑', 'characters for ascending sort') # ↑▲↟↥↾↿⇞⇡⇧⇈⤉⤒⥔⥘⥜⥠⍏˄ˆ
36
+ vd.theme_option('disp_sort_desc', '↓↡⇟⇣⇩⇓', 'characters for descending sort') # ↓▼↡↧⇂⇃⇟⇣⇩⇊⤈⤓⥕⥙⥝⥡⍖˅ˇ
37
+ vd.theme_option('color_default', 'white on black', 'the default fg and bg colors')
38
+ vd.theme_option('color_default_hdr', 'bold', 'color of the column headers')
39
+ vd.theme_option('color_bottom_hdr', 'underline', 'color of the bottom header row')
40
+ vd.theme_option('color_current_row', 'reverse', 'color of the cursor row')
41
+ vd.theme_option('color_current_col', 'bold', 'color of the cursor column')
42
+ vd.theme_option('color_current_cell', '', 'color of current cell, if different from color_current_row+color_current_col')
43
+ vd.theme_option('color_current_hdr', 'bold reverse', 'color of the header for the cursor column')
44
+ vd.theme_option('color_column_sep', '246 blue', 'color of column separators')
45
+ vd.theme_option('color_key_col', '81 cyan', 'color of key columns')
46
+ vd.theme_option('color_hidden_col', '8', 'color of hidden columns on metasheets')
47
+ vd.theme_option('color_selected_row', '215 yellow', 'color of selected rows')
48
+ vd.theme_option('color_clickable', 'underline', 'color of internally clickable item')
49
+ vd.theme_option('color_code', 'bold white on 237', 'color of code sample')
50
+ vd.theme_option('color_heading', 'bold 200', 'color of header')
51
+ vd.theme_option('color_guide_unwritten', '243 on black', 'color of unwritten guides in GuideGuide')
52
+
53
+ vd.theme_option('force_256_colors', False, 'use 256 colors even if curses reports fewer')
54
+
55
+ vd.option('quitguard', False, 'confirm before quitting modified sheet')
56
+ vd.option('default_width', 20, 'default column width', replay=True, max_help=1) # TODO: make not replay and remove from markdown saver
57
+ vd.option('default_height', 4, 'default column height', max_help=-1)
58
+ vd.option('textwrap_cells', True, 'wordwrap text for multiline rows', max_help=1)