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.
- visidata/__init__.py +72 -91
- visidata/_input.py +259 -42
- visidata/_open.py +84 -29
- visidata/_types.py +21 -3
- visidata/_urlcache.py +17 -4
- visidata/aggregators.py +65 -25
- visidata/apps/__init__.py +0 -0
- visidata/apps/vdsql/__about__.py +8 -0
- visidata/apps/vdsql/__init__.py +5 -0
- visidata/apps/vdsql/__main__.py +27 -0
- visidata/apps/vdsql/_ibis.py +748 -0
- visidata/apps/vdsql/bigquery.py +61 -0
- visidata/apps/vdsql/clickhouse.py +53 -0
- visidata/apps/vdsql/setup.py +40 -0
- visidata/apps/vdsql/snowflake.py +67 -0
- visidata/apps/vgit/__init__.py +13 -0
- {vgit → visidata/apps/vgit}/blame.py +5 -2
- {vgit → visidata/apps/vgit}/branch.py +31 -16
- {vgit → visidata/apps/vgit}/config.py +3 -3
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- {vgit → visidata/apps/vgit}/grep.py +6 -5
- visidata/apps/vgit/log.py +81 -0
- {vgit → visidata/apps/vgit}/main.py +18 -5
- {vgit → visidata/apps/vgit}/remote.py +8 -4
- visidata/apps/vgit/repos.py +71 -0
- {vgit → visidata/apps/vgit}/setup.py +6 -4
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- {vgit → visidata/apps/vgit}/statusbar.py +2 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +208 -93
- visidata/choose.py +6 -6
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +73 -17
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +88 -114
- visidata/color.py +142 -56
- visidata/column.py +121 -129
- visidata/ddw/input.ddw +74 -79
- visidata/ddw/regex.ddw +57 -0
- visidata/ddwplay.py +33 -14
- visidata/deprecated.py +77 -3
- visidata/desktop/visidata.desktop +7 -0
- visidata/editor.py +12 -6
- visidata/errors.py +5 -1
- visidata/experimental/__init__.py +0 -0
- visidata/experimental/diff_sheet.py +29 -0
- visidata/experimental/digit_autoedit.py +6 -0
- visidata/experimental/gdrive.py +89 -0
- visidata/experimental/google.py +37 -0
- visidata/experimental/gsheets.py +79 -0
- visidata/experimental/live_search.py +37 -0
- visidata/experimental/liveupdate.py +45 -0
- visidata/experimental/mark.py +133 -0
- visidata/experimental/noahs_tapestry/__init__.py +1 -0
- visidata/experimental/noahs_tapestry/tapestry.py +147 -0
- visidata/experimental/rownum.py +73 -0
- visidata/experimental/slide_cells.py +26 -0
- visidata/expr.py +8 -4
- visidata/extensible.py +30 -5
- visidata/features/__init__.py +0 -0
- visidata/features/addcol_audiometadata.py +42 -0
- visidata/features/addcol_histogram.py +34 -0
- visidata/features/canvas_save_svg.py +69 -0
- visidata/features/change_precision.py +46 -0
- visidata/features/cmdpalette.py +163 -0
- visidata/features/colorbrewer.py +363 -0
- visidata/{colorsheet.py → features/colorsheet.py} +17 -16
- visidata/features/command_server.py +105 -0
- visidata/features/currency_to_usd.py +70 -0
- visidata/{customdate.py → features/customdate.py} +2 -0
- visidata/features/dedupe.py +132 -0
- visidata/{describe.py → features/describe.py} +17 -15
- visidata/features/errors_guide.py +26 -0
- visidata/features/expand_cols.py +202 -0
- visidata/{fill.py → features/fill.py} +3 -1
- visidata/{freeze.py → features/freeze.py} +11 -6
- visidata/features/graph_seaborn.py +79 -0
- visidata/features/helloworld.py +10 -0
- visidata/features/hint_types.py +17 -0
- visidata/{incr.py → features/incr.py} +5 -0
- visidata/{join.py → features/join.py} +107 -53
- visidata/features/known_cols.py +21 -0
- visidata/features/layout.py +62 -0
- visidata/{melt.py → features/melt.py} +32 -21
- visidata/features/normcol.py +118 -0
- visidata/features/open_config.py +7 -0
- visidata/features/open_syspaste.py +18 -0
- visidata/features/ping.py +157 -0
- visidata/features/procmgr.py +208 -0
- visidata/features/random_sample.py +6 -0
- visidata/{regex.py → features/regex.py} +47 -31
- visidata/features/reload_every.py +55 -0
- visidata/features/rename_col_cascade.py +30 -0
- visidata/features/scroll_context.py +60 -0
- visidata/features/select_equal_selected.py +11 -0
- visidata/features/setcol_fake.py +65 -0
- visidata/{slide.py → features/slide.py} +75 -21
- visidata/features/sparkline.py +48 -0
- visidata/features/status_source.py +20 -0
- visidata/{sysedit.py → features/sysedit.py} +2 -1
- visidata/features/sysopen_mailcap.py +46 -0
- visidata/features/term_extras.py +13 -0
- visidata/{transpose.py → features/transpose.py} +5 -4
- visidata/features/type_ipaddr.py +73 -0
- visidata/features/type_url.py +11 -0
- visidata/{unfurl.py → features/unfurl.py} +9 -9
- visidata/{window.py → features/window.py} +2 -2
- visidata/form.py +50 -21
- visidata/freqtbl.py +81 -33
- visidata/fuzzymatch.py +414 -0
- visidata/graph.py +105 -33
- visidata/guide.py +180 -0
- visidata/help.py +75 -44
- visidata/hint.py +39 -0
- visidata/indexsheet.py +109 -0
- visidata/input_history.py +55 -0
- visidata/interface.py +58 -0
- visidata/keys.py +17 -16
- visidata/loaders/__init__.py +9 -0
- visidata/loaders/_pandas.py +61 -21
- visidata/loaders/api_airtable.py +70 -0
- visidata/loaders/api_bitio.py +102 -0
- visidata/loaders/api_matrix.py +148 -0
- visidata/loaders/api_reddit.py +306 -0
- visidata/loaders/api_zulip.py +249 -0
- visidata/loaders/archive.py +41 -7
- visidata/loaders/arrow.py +7 -7
- visidata/loaders/conll.py +49 -0
- visidata/loaders/csv.py +25 -7
- visidata/loaders/eml.py +3 -4
- visidata/loaders/f5log.py +1204 -0
- visidata/loaders/fec.py +325 -0
- visidata/loaders/fixed_width.py +2 -4
- visidata/loaders/frictionless.py +3 -3
- visidata/loaders/geojson.py +8 -5
- visidata/loaders/google.py +48 -0
- visidata/loaders/graphviz.py +4 -4
- visidata/loaders/hdf5.py +4 -4
- visidata/loaders/html.py +48 -10
- visidata/loaders/http.py +84 -30
- visidata/loaders/imap.py +20 -10
- visidata/loaders/jrnl.py +52 -0
- visidata/loaders/json.py +83 -29
- visidata/loaders/jsonla.py +74 -0
- visidata/loaders/lsv.py +15 -11
- visidata/loaders/mailbox.py +40 -0
- visidata/loaders/markdown.py +1 -3
- visidata/loaders/mbtiles.py +4 -5
- visidata/loaders/mysql.py +11 -13
- visidata/loaders/npy.py +7 -7
- visidata/loaders/odf.py +4 -1
- visidata/loaders/orgmode.py +428 -0
- visidata/loaders/pandas_freqtbl.py +14 -20
- visidata/loaders/parquet.py +62 -6
- visidata/loaders/pcap.py +3 -3
- visidata/loaders/pdf.py +4 -3
- visidata/loaders/png.py +19 -13
- visidata/loaders/postgres.py +9 -8
- visidata/loaders/rec.py +7 -3
- visidata/loaders/s3.py +342 -0
- visidata/loaders/sas.py +5 -5
- visidata/loaders/scrape.py +186 -0
- visidata/loaders/shp.py +6 -5
- visidata/loaders/spss.py +5 -6
- visidata/loaders/sqlite.py +68 -28
- visidata/loaders/texttables.py +1 -1
- visidata/loaders/toml.py +60 -0
- visidata/loaders/tsv.py +61 -19
- visidata/loaders/ttf.py +19 -7
- visidata/loaders/unzip_http.py +6 -5
- visidata/loaders/usv.py +1 -1
- visidata/loaders/vcf.py +16 -16
- visidata/loaders/vds.py +10 -7
- visidata/loaders/vdx.py +30 -5
- visidata/loaders/xlsb.py +8 -1
- visidata/loaders/xlsx.py +145 -25
- visidata/loaders/xml.py +6 -3
- visidata/loaders/xword.py +4 -4
- visidata/loaders/yaml.py +15 -5
- visidata/macros.py +129 -42
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -155
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +301 -148
- visidata/man/vd.txt +290 -153
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +78 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +113 -32
- visidata/pivot.py +73 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +37 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -25
- visidata/sheets.py +229 -257
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +113 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +62 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +17 -0
- visidata/tests/test_menu.py +14 -0
- visidata/tests/test_path.py +13 -4
- visidata/text_source.py +53 -0
- visidata/textsheet.py +10 -3
- visidata/theme.py +44 -0
- visidata/themes/__init__.py +0 -0
- visidata/themes/ascii8.py +84 -0
- visidata/themes/asciimono.py +84 -0
- visidata/themes/light.py +17 -0
- visidata/threads.py +87 -39
- visidata/tuiwin.py +22 -0
- visidata/type_currency.py +22 -3
- visidata/type_date.py +31 -9
- visidata/type_floatsi.py +5 -1
- visidata/undo.py +17 -5
- visidata/utils.py +106 -23
- visidata/vdobj.py +28 -17
- visidata/windows.py +10 -0
- visidata/wrappers.py +9 -3
- visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/vd.1 +301 -148
- {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +301 -148
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/METADATA +12 -8
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- vgit/__init__.py +0 -1
- vgit/gitsheet.py +0 -164
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.1.data/scripts/vgit +0 -9
- visidata-2.11.1.dist-info/RECORD +0 -155
- {vgit → visidata/apps/vgit}/__main__.py +0 -0
- {vgit → visidata/apps/vgit}/abort.py +0 -0
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.1.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/selection.py
CHANGED
@@ -1,10 +1,66 @@
|
|
1
|
-
from
|
1
|
+
from copy import copy
|
2
|
+
from visidata import vd, Sheet, Progress, asyncthread, options, rotateRange, Fanout, undoAttrCopyFunc, RowColorizer, GuideSheet
|
2
3
|
|
3
4
|
vd.option('bulk_select_clear', False, 'clear selected rows before new bulk selections', replay=True)
|
4
5
|
vd.option('some_selected_rows', False, 'if no rows selected, if True, someSelectedRows returns all rows; if False, fails')
|
5
6
|
|
6
7
|
Sheet.init('_selectedRows', dict) # rowid(row) -> row
|
7
8
|
|
9
|
+
vd.rowNoters.append(
|
10
|
+
lambda sheet, row: sheet.isSelected(row) and sheet.options.disp_selected_note
|
11
|
+
)
|
12
|
+
Sheet.colorizers.append( RowColorizer(2, 'color_selected_row', lambda s,c,r,v:
|
13
|
+
r is not None and s.isSelected(r))
|
14
|
+
)
|
15
|
+
|
16
|
+
class SelectionGuide(GuideSheet):
|
17
|
+
sheettype = Sheet
|
18
|
+
guide_text ='''# Selecting and filtering
|
19
|
+
|
20
|
+
Some commands operate only on "selected rows". For instance, a common command to filter is {help.commands.dup_selected}.
|
21
|
+
|
22
|
+
Many g-prefixed commands are like this. For example, use {help.commands.edit_cell}, but use {help.commands.setcol_input}. Search for "selected rows" in the [:onclick help-commands-all]commands list[/] or the [:onclick sysopen-help]manpage[/] for a full list.
|
23
|
+
|
24
|
+
Rows on the **Frequency Table** or **Pivot Table** reference a group of rows from the source sheet. Selecting a row on those sheets also selects the referenced rows on the underlying source sheet.
|
25
|
+
|
26
|
+
Select and unselect rows with these commands:
|
27
|
+
|
28
|
+
## One row at a time
|
29
|
+
|
30
|
+
- {help.commands.select_row}
|
31
|
+
- {help.commands.unselect_row}
|
32
|
+
- {help.commands.stoggle_row}
|
33
|
+
|
34
|
+
## All rows at the same time
|
35
|
+
|
36
|
+
- {help.commands.select_rows}
|
37
|
+
- {help.commands.unselect_rows}
|
38
|
+
- {help.commands.stoggle_rows}
|
39
|
+
|
40
|
+
## By matching patterns
|
41
|
+
|
42
|
+
- {help.commands.select_col_regex}
|
43
|
+
- {help.commands.unselect_col_regex}
|
44
|
+
- {help.commands.select_cols_regex}
|
45
|
+
- {help.commands.unselect_cols_regex}
|
46
|
+
|
47
|
+
- {help.commands.select_equal_cell}
|
48
|
+
- {help.commands.select_equal_row}
|
49
|
+
|
50
|
+
## Select by Python expression
|
51
|
+
|
52
|
+
Python expressions can use a column value by the column name, if the
|
53
|
+
column name is a valid Python identifier (with only letters, digits, and underscores).
|
54
|
+
|
55
|
+
- {help.commands.select_expr}
|
56
|
+
- {help.commands.unselect_expr}
|
57
|
+
|
58
|
+
## Options
|
59
|
+
|
60
|
+
- {help.options.bulk_select_clear}
|
61
|
+
- {help.options.some_selected_rows}
|
62
|
+
'''
|
63
|
+
|
8
64
|
@Sheet.api
|
9
65
|
def isSelected(self, row):
|
10
66
|
'Return True if *row* is selected.'
|
@@ -174,10 +230,10 @@ Sheet.addCommand('gzt', 'stoggle-after', 'toggle(rows[cursorRowIndex:])', 'toggl
|
|
174
230
|
Sheet.addCommand('gzs', 'select-after', 'select(rows[cursorRowIndex:])', 'select all rows from cursor to bottom')
|
175
231
|
Sheet.addCommand('gzu', 'unselect-after', 'unselect(rows[cursorRowIndex:])', 'unselect all rows from cursor to bottom')
|
176
232
|
|
177
|
-
Sheet.addCommand('|', 'select-col-regex', 'selectByIdx(
|
178
|
-
Sheet.addCommand('\\', 'unselect-col-regex', 'unselectByIdx(
|
179
|
-
Sheet.addCommand('g|', 'select-cols-regex', 'selectByIdx(
|
180
|
-
Sheet.addCommand('g\\', 'unselect-cols-regex', 'unselectByIdx(
|
233
|
+
Sheet.addCommand('|', 'select-col-regex', 'selectByIdx(searchInputRegex("select", columns="cursorCol"))', 'select rows matching regex in current column')
|
234
|
+
Sheet.addCommand('\\', 'unselect-col-regex', 'unselectByIdx(searchInputRegex("unselect", columns="cursorCol"))', 'unselect rows matching regex in current column')
|
235
|
+
Sheet.addCommand('g|', 'select-cols-regex', 'selectByIdx(searchInputRegex("select", columns="visibleCols"))', 'select rows matching regex in any visible column')
|
236
|
+
Sheet.addCommand('g\\', 'unselect-cols-regex', 'unselectByIdx(searchInputRegex("unselect", columns="visibleCols"))', 'unselect rows matching regex in any visible column')
|
181
237
|
|
182
238
|
Sheet.addCommand(',', 'select-equal-cell', 'select(gatherBy(lambda r,c=cursorCol,v=cursorDisplay: c.getDisplayValue(r) == v), progress=False)', 'select rows matching current cell in current column')
|
183
239
|
Sheet.addCommand('g,', 'select-equal-row', 'select(gatherBy(lambda r,currow=cursorRow,vcols=visibleCols: all([c.getDisplayValue(r) == c.getDisplayValue(currow) for c in vcols])), progress=False)', 'select rows matching current row in all visible columns')
|
@@ -189,3 +245,26 @@ Sheet.addCommand('z\\', 'unselect-expr', 'expr=inputExpr("unselect by expr: ");
|
|
189
245
|
|
190
246
|
Sheet.addCommand(None, 'select-error-col', 'select(gatherBy(lambda r,c=cursorCol: c.isError(r)), progress=False)', 'select rows with errors in current column')
|
191
247
|
Sheet.addCommand(None, 'select-error', 'select(gatherBy(lambda r,vcols=visibleCols: isinstance(r, TypedExceptionWrapper) or any([c.isError(r) for c in vcols])), progress=False)', 'select rows with errors in any column')
|
248
|
+
|
249
|
+
vd.addMenuItems('''
|
250
|
+
Row > Select > current row > select-row
|
251
|
+
Row > Select > all rows > select-rows
|
252
|
+
Row > Select > from top > select-before
|
253
|
+
Row > Select > to bottom > select-after
|
254
|
+
Row > Select > by Python expr > select-expr
|
255
|
+
Row > Select > equal to current cell > select-equal-cell
|
256
|
+
Row > Select > equal to current row > select-equal-row
|
257
|
+
Row > Select > errors > current column > select-error-col
|
258
|
+
Row > Select > errors > any column > select-error
|
259
|
+
Row > Unselect > current row > unselect-row
|
260
|
+
Row > Unselect > all rows > unselect-rows
|
261
|
+
Row > Unselect > from top > unselect-before
|
262
|
+
Row > Unselect > to bottom > unselect-after
|
263
|
+
Row > Unselect > by Python expr > unselect-expr
|
264
|
+
Row > Toggle select > current row > stoggle-row
|
265
|
+
Row > Toggle select > all rows > stoggle-rows
|
266
|
+
Row > Toggle select > from top > stoggle-before
|
267
|
+
Row > Toggle select > to bottom > stoggle-after
|
268
|
+
''')
|
269
|
+
|
270
|
+
vd.addGuide('SelectionGuide', SelectionGuide)
|
visidata/settings.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import collections
|
2
|
+
import functools
|
2
3
|
import sys
|
3
4
|
import inspect
|
4
5
|
import argparse
|
@@ -16,6 +17,12 @@ class SettingsMgr(collections.OrderedDict):
|
|
16
17
|
super().__init__()
|
17
18
|
self.allobjs = {}
|
18
19
|
|
20
|
+
def __hash__(self):
|
21
|
+
return hash(id(self))
|
22
|
+
|
23
|
+
def __eq__(self, other):
|
24
|
+
return self is other
|
25
|
+
|
19
26
|
def objname(self, obj):
|
20
27
|
if isinstance(obj, str):
|
21
28
|
v = obj
|
@@ -51,6 +58,7 @@ class SettingsMgr(collections.OrderedDict):
|
|
51
58
|
def setdefault(self, k, v):
|
52
59
|
return self.set(k, v, 'default')
|
53
60
|
|
61
|
+
@functools.lru_cache()
|
54
62
|
def _mappings(self, obj):
|
55
63
|
'''Return list of contexts in order to resolve settings. ordering is, from lowest to highest precedence:
|
56
64
|
|
@@ -65,7 +73,7 @@ class SettingsMgr(collections.OrderedDict):
|
|
65
73
|
'''
|
66
74
|
mappings = []
|
67
75
|
if obj:
|
68
|
-
mappings += [
|
76
|
+
mappings += [obj]
|
69
77
|
mappings += [self.objname(cls) for cls in inspect.getmro(type(obj))]
|
70
78
|
|
71
79
|
mappings += ['global', 'default']
|
@@ -75,7 +83,7 @@ class SettingsMgr(collections.OrderedDict):
|
|
75
83
|
d = self.get(key, None)
|
76
84
|
if d:
|
77
85
|
for m in self._mappings(obj or vd.activeSheet):
|
78
|
-
v = d.get(m)
|
86
|
+
v = d.get(self.objname(m))
|
79
87
|
if v:
|
80
88
|
return v
|
81
89
|
|
@@ -85,6 +93,7 @@ class SettingsMgr(collections.OrderedDict):
|
|
85
93
|
obj = vd.activeSheet
|
86
94
|
|
87
95
|
for o in self._mappings(obj):
|
96
|
+
o = self.objname(o)
|
88
97
|
for k in self.keys():
|
89
98
|
for o2 in self[k]:
|
90
99
|
if o == o2:
|
@@ -98,19 +107,25 @@ class SettingsMgr(collections.OrderedDict):
|
|
98
107
|
|
99
108
|
|
100
109
|
class Command:
|
101
|
-
def __init__(self, longname, execstr, helpstr=''):
|
110
|
+
def __init__(self, longname, execstr, helpstr='', module='', deprecated=False):
|
102
111
|
self.longname = longname
|
103
112
|
self.execstr = execstr
|
104
113
|
self.helpstr = helpstr
|
114
|
+
self.module = module
|
115
|
+
self.deprecated = deprecated
|
105
116
|
|
106
117
|
|
107
118
|
class Option:
|
108
|
-
def __init__(self, name, value,
|
119
|
+
def __init__(self, name, value, description='', module='', help='', max_help=10):
|
120
|
+
# description gets shows on the manpage and the optionssheet; help is shown on the sidebar while editing
|
109
121
|
self.name = name
|
110
122
|
self.value = value
|
111
|
-
self.helpstr =
|
123
|
+
self.helpstr = description
|
124
|
+
self.extrahelp = help
|
112
125
|
self.replayable = False
|
113
126
|
self.sheettype = BaseSheet
|
127
|
+
self.module = module
|
128
|
+
self.max_help = max_help
|
114
129
|
|
115
130
|
def __str__(self):
|
116
131
|
return str(self.value)
|
@@ -140,10 +155,12 @@ class OptionsObject:
|
|
140
155
|
self._cache[(k, obj or vd.activeSheet)] = opt
|
141
156
|
return opt
|
142
157
|
|
143
|
-
def _set(self, k, v, obj=None, helpstr=''):
|
144
|
-
|
158
|
+
def _set(self, k, v, obj=None, helpstr='', module=None):
|
159
|
+
k, v = vd._resolve_optalias(k, v) # to set deprecated and abbreviated options
|
160
|
+
|
161
|
+
opt = self._get(k) or Option(k, v, '', module)
|
145
162
|
self._cache.clear() # invalidate entire cache on any change
|
146
|
-
return self._opts.set(k, Option(k, v, helpstr), obj)
|
163
|
+
return self._opts.set(k, Option(k, v, opt.helpstr or helpstr, opt.module or module), obj)
|
147
164
|
|
148
165
|
def is_set(self, k, obj=None):
|
149
166
|
d = self._opts.get(k, None)
|
@@ -176,6 +193,7 @@ class OptionsObject:
|
|
176
193
|
def set(self, optname, value, obj='global'):
|
177
194
|
"Override *value* for *optname* in the options context, or in the *obj* context if given."
|
178
195
|
opt = self._get(optname)
|
196
|
+
module = None # keep default
|
179
197
|
if opt:
|
180
198
|
curval = opt.value
|
181
199
|
t = type(curval)
|
@@ -193,30 +211,44 @@ class OptionsObject:
|
|
193
211
|
if curval != value and self._get(optname, 'default').replayable:
|
194
212
|
if obj != 'default' and type(obj) is not type: # default and class options set on init aren't recorded
|
195
213
|
if vd.cmdlog:
|
196
|
-
|
197
|
-
vd.cmdlog.addRow(vd.cmdlog.newRow(sheet=objname, row=optname,
|
198
|
-
keystrokes='', input=str(value),
|
199
|
-
longname='set-option', undofuncs=[]))
|
214
|
+
self.add_option_to_cmdlogs(obj, optname, value, 'set-option')
|
200
215
|
else:
|
201
216
|
curval = None
|
202
217
|
vd.warning('setting unknown option %s' % optname)
|
218
|
+
module = 'unknown'
|
203
219
|
|
204
|
-
return self._set(optname, value, obj)
|
220
|
+
return self._set(optname, value, obj, module=module)
|
205
221
|
|
206
222
|
def unset(self, optname, obj=None):
|
207
223
|
'Remove setting value for given context.'
|
208
224
|
v = self._opts.unset(optname, obj)
|
209
225
|
opt = self._get(optname)
|
210
226
|
if vd.cmdlog and opt and opt.replayable:
|
211
|
-
|
212
|
-
vd.cmdlog.addRow(vd.cmdlog.newRow(sheet=objname, row=optname,
|
213
|
-
keystrokes='', input='',
|
214
|
-
longname='unset-option'))
|
227
|
+
self.add_option_to_cmdlogs(obj, optname, value='', longname='unset-option')
|
215
228
|
self._cache.clear() # invalidate entire cache on any change
|
216
229
|
return v
|
217
230
|
|
218
|
-
def
|
219
|
-
|
231
|
+
def add_option_to_cmdlogs(self, obj, optname, value='', longname='set-option'):
|
232
|
+
'Records option-set on cmdlogs'
|
233
|
+
objname = self._opts.objname(obj)
|
234
|
+
# all options are recorded on global cmdlog
|
235
|
+
vd.cmdlog.addRow(vd.cmdlog.newRow(sheet=objname, row=optname,
|
236
|
+
keystrokes='', input=str(value),
|
237
|
+
longname=longname, undofuncs=[]))
|
238
|
+
# global options are recorded on all cmdlog_sheet's
|
239
|
+
if obj == 'global':
|
240
|
+
for vs in vd.sheets:
|
241
|
+
vs.cmdlog_sheet.addRow(vd.cmdlog.newRow(sheet=objname, row=optname,
|
242
|
+
keystrokes='', input=str(value),
|
243
|
+
longname=longname, undofuncs=[]))
|
244
|
+
# sheet-specific options are recorded on that sheet
|
245
|
+
elif isinstance(obj, BaseSheet):
|
246
|
+
obj.cmdlog_sheet.addRow(vd.cmdlog.newRow(sheet=objname, row=optname,
|
247
|
+
keystrokes='', input=str(value),
|
248
|
+
longname=longname, undofuncs=[]))
|
249
|
+
|
250
|
+
def setdefault(self, optname, value, helpstr, module):
|
251
|
+
return self._set(optname, value, 'default', helpstr=helpstr, module=module)
|
220
252
|
|
221
253
|
def getall(self, prefix=''):
|
222
254
|
'Return dictionary of all options beginning with `prefix` (with `prefix` removed from the name).'
|
@@ -247,10 +279,27 @@ vd.bindkeys = SettingsMgr()
|
|
247
279
|
vd._options = SettingsMgr()
|
248
280
|
|
249
281
|
vd.options = vd.OptionsObject(vd._options) # global option settings
|
282
|
+
vd.option_aliases = {}
|
250
283
|
|
251
284
|
|
252
285
|
@VisiData.api
|
253
|
-
def
|
286
|
+
def optalias(vd, altname, optname, val=None):
|
287
|
+
'Create an alias `altname` for option `optname`, setting the value to a particular `val` (if not None).'
|
288
|
+
vd.option_aliases[altname] = (optname, val)
|
289
|
+
|
290
|
+
|
291
|
+
@VisiData.api
|
292
|
+
def _resolve_optalias(vd, optname, optval):
|
293
|
+
while optname in vd.option_aliases:
|
294
|
+
optname, v = vd.option_aliases.get(optname)
|
295
|
+
if v is not None: # value might be given
|
296
|
+
optval = v
|
297
|
+
|
298
|
+
return optname, optval
|
299
|
+
|
300
|
+
|
301
|
+
@VisiData.api
|
302
|
+
def option(vd, name, default, description, replay=False, sheettype=BaseSheet, help:str='', max_help=10):
|
254
303
|
'''Declare a new option.
|
255
304
|
|
256
305
|
- `name`: name of option
|
@@ -259,12 +308,21 @@ def option(vd, name, default, helpstr, replay=False, sheettype=BaseSheet):
|
|
259
308
|
- `replay`: ``True`` if changes to the option should be stored in the **Command Log**
|
260
309
|
- `sheettype`: ``None`` if the option is not sheet-specific, to make it global on CLI
|
261
310
|
'''
|
262
|
-
opt = vd.options.setdefault(name, default,
|
311
|
+
opt = vd.options.setdefault(name, default, description, vd.importingModule)
|
263
312
|
opt.replayable = replay
|
264
313
|
opt.sheettype=sheettype
|
314
|
+
opt.extrahelp = help
|
315
|
+
opt.max_help = max_help
|
265
316
|
return opt
|
266
317
|
|
267
318
|
|
319
|
+
@VisiData.api
|
320
|
+
def theme_option(vd, name, *args, **kwargs):
|
321
|
+
if name.startswith('color_'):
|
322
|
+
kwargs.setdefault('help', vd.help_color)
|
323
|
+
return vd.option(name, *args, **kwargs, max_help=-1)
|
324
|
+
|
325
|
+
|
268
326
|
@BaseSheet.class_api
|
269
327
|
@classmethod
|
270
328
|
def addCommand(cls, keystrokes, longname, execstr, helpstr='', **kwargs):
|
@@ -275,9 +333,9 @@ def addCommand(cls, keystrokes, longname, execstr, helpstr='', **kwargs):
|
|
275
333
|
- *execstr*: Python statement to pass to `exec()`'ed when the command is executed.
|
276
334
|
- *helpstr*: help string shown in the **Commands Sheet**.
|
277
335
|
'''
|
278
|
-
vd.commands.set(longname, Command(longname, execstr, helpstr=helpstr, **kwargs), cls)
|
336
|
+
vd.commands.set(longname, Command(longname, execstr, helpstr=helpstr, module=vd.importingModule, **kwargs), cls)
|
279
337
|
if keystrokes:
|
280
|
-
vd.bindkeys.set(vd.prettykeys(keystrokes), longname, cls)
|
338
|
+
vd.bindkeys.set(vd.prettykeys(keystrokes.replace(' ', '')), longname, cls)
|
281
339
|
return longname
|
282
340
|
|
283
341
|
def _command(cls, binding, longname, helpstr, **kwargs):
|
@@ -333,9 +391,12 @@ def loadConfigFile(vd, fn='', _globals=None):
|
|
333
391
|
try:
|
334
392
|
with open(p) as fd:
|
335
393
|
code = compile(fd.read(), str(p), 'exec')
|
394
|
+
vd.importingModule = 'visidatarc'
|
336
395
|
exec(code, _globals)
|
337
396
|
except Exception as e:
|
338
397
|
vd.exceptionCaught(e)
|
398
|
+
finally:
|
399
|
+
vd.importingModule = None
|
339
400
|
|
340
401
|
vd.addGlobals(_globals)
|
341
402
|
|
@@ -385,13 +446,15 @@ def loadConfigAndPlugins(vd, args=AttrDict()):
|
|
385
446
|
|
386
447
|
for ep in eps_visidata:
|
387
448
|
try:
|
449
|
+
vd.importingModule = ep.name
|
388
450
|
plug = ep.load()
|
389
451
|
sys.modules[f'visidata.plugins.{ep.name}'] = plug
|
390
452
|
vd.debug(f'Plugin {ep.name} loaded')
|
391
453
|
except Exception as e:
|
392
454
|
vd.warning(f'Plugin {ep.name} failed to load')
|
393
455
|
vd.exceptionCaught(e)
|
394
|
-
|
456
|
+
finally:
|
457
|
+
vd.importingModule = None
|
395
458
|
|
396
459
|
# import plugins from .visidata/plugins before .visidatarc, so plugin options can be overridden
|
397
460
|
for modname in (args.imports or vd.options.imports or '').split():
|
@@ -410,10 +473,84 @@ def loadConfigAndPlugins(vd, args=AttrDict()):
|
|
410
473
|
vd.loadConfigFile(vd.options.config, vd.getGlobals())
|
411
474
|
|
412
475
|
|
476
|
+
@VisiData.api
|
477
|
+
def importModule(vd, pkgname):
|
478
|
+
'Import the given *pkgname*, setting vd.importingModule to *pkgname* before import and resetting to None after.'
|
479
|
+
modparts = pkgname.split('.')
|
480
|
+
vd.importingModule = modparts[-1]
|
481
|
+
r = importlib.import_module(pkgname)
|
482
|
+
vd.importingModule = None
|
483
|
+
vd.importedModules.append(r)
|
484
|
+
return r
|
485
|
+
|
486
|
+
|
487
|
+
@VisiData.api
|
488
|
+
def importSubmodules(vd, pkgname):
|
489
|
+
'Import all files below the given *pkgname*'
|
490
|
+
import pkgutil
|
491
|
+
import os.path
|
492
|
+
|
493
|
+
m = vd.importModule(pkgname)
|
494
|
+
for module in pkgutil.walk_packages(m.__path__):
|
495
|
+
vd.importModule(pkgname + '.' + module.name)
|
496
|
+
|
497
|
+
|
498
|
+
@VisiData.api
|
499
|
+
def importStar(vd, pkgname):
|
500
|
+
'Add all symbols from *pkgname* into visidata globals.'
|
501
|
+
import pkgutil
|
502
|
+
import os.path
|
503
|
+
|
504
|
+
m = vd.importModule(pkgname)
|
505
|
+
vd.addGlobals({pkgname:m})
|
506
|
+
vd.addGlobals(m.__dict__)
|
507
|
+
|
508
|
+
|
509
|
+
@VisiData.api
|
510
|
+
def importExternal(vd, modname, pipmodname=''):
|
511
|
+
pipmodname = pipmodname or modname
|
512
|
+
try:
|
513
|
+
m = importlib.import_module(modname)
|
514
|
+
vd.addGlobals({modname:m})
|
515
|
+
return m
|
516
|
+
except ModuleNotFoundError as e:
|
517
|
+
vd.fail(f'External package "{modname}" not installed; run: pip install {pipmodname}')
|
518
|
+
|
519
|
+
|
520
|
+
@VisiData.api
|
521
|
+
def requireOptions(vd, *args, help=''):
|
522
|
+
'''Prompt user to input values for option names in *args* if current values
|
523
|
+
are non-false. Offer to persist the values to visidatarc.'''
|
524
|
+
|
525
|
+
optvals = {}
|
526
|
+
for optname in args:
|
527
|
+
if not getattr(vd.options, optname):
|
528
|
+
if help:
|
529
|
+
vd.status(help)
|
530
|
+
v = vd.input(f'{optname}: ', record=False, display='password' not in optname)
|
531
|
+
optvals[optname] = v
|
532
|
+
|
533
|
+
vd.setPersistentOptions(**optvals)
|
534
|
+
|
535
|
+
|
536
|
+
@VisiData.api
|
537
|
+
def setPersistentOptions(vd, **kwargs):
|
538
|
+
'''Set options from *kwargs* and offer to save them to visidatarc.'''
|
539
|
+
for optname, optval in kwargs.items():
|
540
|
+
setattr(vd.options, optname, optval)
|
541
|
+
|
542
|
+
optnames = ' '.join(kwargs.keys())
|
543
|
+
yn = vd.input(f'Save {len(kwargs)} options ({optnames}) to {vd.options.config}? ', record=False)[0:1]
|
544
|
+
|
545
|
+
if yn and yn in 'Yy':
|
546
|
+
with open(str(visidata.Path(vd.options.config)), mode='a') as fp:
|
547
|
+
for optname, optval in kwargs.items():
|
548
|
+
fp.write(f'options.{optname}={repr(optval)}\n')
|
549
|
+
|
550
|
+
|
413
551
|
vd.option('visidata_dir', '~/.visidata/', 'directory to load and store additional files', sheettype=None)
|
414
552
|
|
415
553
|
BaseSheet.bindkey('^M', '^J') # for windows ENTER
|
416
|
-
BaseSheet.addCommand('gO', 'open-config', 'vd.push(open_txt(Path(options.config)))', 'open options.config as text sheet')
|
417
554
|
|
418
555
|
vd.addGlobals({
|
419
556
|
'options': vd.options, # legacy
|