visidata 2.11.dev0__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 +263 -44
- visidata/_open.py +84 -29
- visidata/_types.py +22 -4
- 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
- visidata/apps/vgit/__main__.py +3 -0
- visidata/apps/vgit/abort.py +23 -0
- visidata/apps/vgit/blame.py +76 -0
- visidata/apps/vgit/branch.py +153 -0
- visidata/apps/vgit/config.py +95 -0
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- visidata/apps/vgit/grep.py +37 -0
- visidata/apps/vgit/log.py +81 -0
- visidata/apps/vgit/main.py +55 -0
- visidata/apps/vgit/remote.py +57 -0
- visidata/apps/vgit/repos.py +71 -0
- visidata/apps/vgit/setup.py +37 -0
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- visidata/apps/vgit/statusbar.py +34 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +251 -99
- visidata/choose.py +15 -11
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +84 -18
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +89 -114
- visidata/color.py +142 -56
- visidata/column.py +134 -131
- 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 +32 -6
- 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} +4 -2
- 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} +33 -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 +3 -5
- 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/macos.py +1 -1
- visidata/macros.py +130 -41
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -154
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +302 -147
- visidata/man/vd.txt +291 -151
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +79 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +131 -43
- visidata/pivot.py +74 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +42 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -24
- 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 +18 -6
- 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.dev0.data → visidata-3.0.data}/data/share/man/man1/vd.1 +302 -147
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +302 -147
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/METADATA +13 -11
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -1
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.dev0.dist-info/RECORD +0 -142
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/macos.py
CHANGED
@@ -69,7 +69,7 @@ BaseSheet.bindkey('¶', 'Alt+7')
|
|
69
69
|
BaseSheet.bindkey('•', 'Alt+8')
|
70
70
|
BaseSheet.bindkey('ª', 'Alt+9')
|
71
71
|
BaseSheet.bindkey('º', 'Alt+0')
|
72
|
-
BaseSheet.bindkey('', 'Alt+`')
|
72
|
+
#BaseSheet.bindkey('', 'Alt+`')
|
73
73
|
BaseSheet.bindkey('–', 'Alt+-')
|
74
74
|
BaseSheet.bindkey('≠', 'Alt+=')
|
75
75
|
BaseSheet.bindkey('“', 'Alt+[')
|
visidata/macros.py
CHANGED
@@ -1,89 +1,178 @@
|
|
1
|
-
from
|
1
|
+
from copy import copy
|
2
2
|
from functools import wraps
|
3
3
|
|
4
4
|
from visidata.cmdlog import CommandLog, CommandLogJsonl
|
5
|
+
from visidata import vd, UNLOADED, asyncthread, vlen
|
6
|
+
from visidata import IndexSheet, VisiData, Sheet, Path, VisiDataMetaSheet, Column, ItemColumn, AttrColumn, BaseSheet, GuideSheet
|
5
7
|
|
6
8
|
vd.macroMode = None
|
7
9
|
vd.macrobindings = {}
|
8
10
|
|
11
|
+
|
12
|
+
vd.macros = vd.StoredList(name='macros')
|
13
|
+
|
14
|
+
|
9
15
|
class MacroSheet(IndexSheet):
|
16
|
+
guide= '''
|
17
|
+
# Macros Sheet
|
18
|
+
This is a list of user-defined macros.
|
19
|
+
|
20
|
+
- `Enter` to open the current macro.
|
21
|
+
- `d` to mark macro for delete; `z Ctrl+S` to commit.
|
22
|
+
'''
|
23
|
+
columns = [
|
24
|
+
AttrColumn('binding'),
|
25
|
+
Column('num_commands', type=vlen, width=0),
|
26
|
+
AttrColumn('source'),
|
27
|
+
]
|
28
|
+
rowtype = 'macros' # rowdef: CommandLogJsonl
|
29
|
+
defer = True
|
30
|
+
nKeys = 1
|
10
31
|
|
11
32
|
def iterload(self):
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
vs = vd.loadInternalSheet(CommandLogJsonl, fp)
|
18
|
-
else:
|
19
|
-
vd.warning(f'failed to load macro {fn}')
|
20
|
-
continue
|
21
|
-
setMacro(ks, vs)
|
22
|
-
yield vs
|
33
|
+
yield from vd.macrobindings.values()
|
34
|
+
|
35
|
+
def commitDeleteRow(self, row):
|
36
|
+
del vd.macrobindings[row.binding]
|
37
|
+
vd.callNoExceptions(Path(row.source).unlink)
|
23
38
|
|
39
|
+
@asyncthread
|
40
|
+
def putChanges(self):
|
41
|
+
self.commitDeletes() #1569 apply deletes early for saveSheets below
|
42
|
+
|
43
|
+
vd.saveSheets(self.source, self, confirm_overwrite=False)
|
44
|
+
self._deferredDels.clear()
|
45
|
+
self.reload()
|
46
|
+
|
47
|
+
def newRow(self):
|
48
|
+
vd.fail('add macros with `m` instead')
|
24
49
|
|
25
50
|
|
26
51
|
@VisiData.lazy_property
|
27
52
|
def macrosheet(vd):
|
28
|
-
|
29
|
-
|
53
|
+
return MacroSheet('user_macros', source=vd.macros.path)
|
54
|
+
|
30
55
|
|
31
|
-
|
32
|
-
|
56
|
+
@VisiData.api
|
57
|
+
def loadMacro(vd, p:Path):
|
58
|
+
if p.exists():
|
59
|
+
if p.ext == 'vd':
|
60
|
+
vs = CommandLog(p.base_stem, source=p)
|
61
|
+
vs.ensureLoaded()
|
62
|
+
return vs
|
63
|
+
elif p.ext == 'vdj':
|
64
|
+
vs = CommandLogJsonl(p.base_stem, source=p)
|
65
|
+
vs.ensureLoaded()
|
66
|
+
return vs
|
67
|
+
|
68
|
+
vd.warning(f'failed to load macro {p}')
|
33
69
|
|
34
|
-
return real_macrosheet
|
35
70
|
|
36
71
|
@VisiData.api
|
37
|
-
def runMacro(vd,
|
38
|
-
vd.replay_sync(
|
72
|
+
def runMacro(vd, binding:str):
|
73
|
+
vd.replay_sync(vd.macrobindings[binding])
|
39
74
|
|
40
|
-
|
75
|
+
|
76
|
+
@VisiData.api
|
77
|
+
def setMacro(vd, ks:str, vs):
|
78
|
+
'Set *ks* which is either a keystroke or a longname to run the cmdlog in *vs*.'
|
79
|
+
vs.binding = ks
|
41
80
|
vd.macrobindings[ks] = vs
|
42
81
|
if vd.isLongname(ks):
|
43
|
-
BaseSheet.addCommand('', ks, 'runMacro(
|
82
|
+
BaseSheet.addCommand('', ks, f'runMacro("{ks}")')
|
44
83
|
else:
|
45
|
-
BaseSheet.addCommand(ks, vs.name, 'runMacro(
|
84
|
+
BaseSheet.addCommand(ks, f'exec-{vs.name}', f'runMacro("{ks}")')
|
46
85
|
|
47
86
|
|
48
87
|
@CommandLogJsonl.api
|
49
88
|
def saveMacro(self, rows, ks):
|
50
89
|
vs = copy(self)
|
51
90
|
vs.rows = rows
|
52
|
-
macropath = Path(vd.fnSuffix(options.visidata_dir
|
91
|
+
macropath = Path(vd.fnSuffix(str(Path(vd.options.visidata_dir)/ks)))
|
53
92
|
vd.save_vdj(macropath, vs)
|
54
|
-
|
55
|
-
vd.
|
56
|
-
vd.
|
57
|
-
vd.
|
93
|
+
vd.status(f'{ks} saved to {macropath}')
|
94
|
+
vd.setMacro(ks, vs)
|
95
|
+
vd.macros.append(dict(binding=ks, source=str(macropath)))
|
96
|
+
vd.reloadMacros()
|
97
|
+
vd.macrosheet.reload()
|
58
98
|
|
59
|
-
|
60
|
-
|
99
|
+
|
100
|
+
# needs to happen before, because the original afterexecsheet resets vd.activecommand to None
|
101
|
+
@CommandLogJsonl.before
|
61
102
|
def afterExecSheet(cmdlog, sheet, escaped, err):
|
62
103
|
if vd.macroMode and (vd.activeCommand is not None) and (vd.activeCommand is not UNLOADED) and (vd.isLoggableCommand(vd.activeCommand.longname)):
|
63
104
|
cmd = copy(vd.activeCommand)
|
64
|
-
cmd.
|
105
|
+
cmd.sheet = ''
|
65
106
|
vd.macroMode.addRow(cmd)
|
66
107
|
|
67
|
-
# the following needs to happen at the end, bc
|
68
|
-
# once cmdlog.afterExecSheet.__wrapped__ runs, vd.activeCommand resets to None
|
69
|
-
cmdlog.afterExecSheet.__wrapped__(cmdlog, sheet, escaped, err)
|
70
108
|
|
71
109
|
@CommandLogJsonl.api
|
72
110
|
def startMacro(cmdlog):
|
111
|
+
if not Path(vd.options.visidata_dir).is_dir():
|
112
|
+
vd.fail(f'create {vd.options.visidata_dir} to save macros')
|
73
113
|
if vd.macroMode:
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
114
|
+
try:
|
115
|
+
ks = vd.input('bind macro to: ', help=f'''
|
116
|
+
# Finish recording macro
|
117
|
+
Type in either a longname like `happy-time` (with at least one hyphen),
|
118
|
+
or spell out a keybinding (like `Alt+b`) manually.
|
119
|
+
|
120
|
+
- Prefixes allowed with a keybinding: `{' '.join(vd.allPrefixes)}`
|
121
|
+
- Press `Ctrl+N` and then press another keystroke to spell that keystroke.
|
122
|
+
- Press `Ctrl+C` to cancel the macro recording.
|
123
|
+
''')
|
124
|
+
while ks in vd.macrobindings:
|
125
|
+
ks = vd.input(f'{ks} already in use; set macro to keybinding: ')
|
126
|
+
vd.cmdlog.saveMacro(vd.macroMode.rows, ks)
|
127
|
+
finally:
|
128
|
+
vd.macroMode = None
|
79
129
|
else:
|
80
|
-
vd.status("recording macro")
|
130
|
+
vd.status("recording macro; stop recording with `m`")
|
81
131
|
vd.macroMode = CommandLogJsonl('current_macro', rows=[])
|
82
132
|
|
133
|
+
|
83
134
|
@VisiData.before
|
135
|
+
@asyncthread
|
84
136
|
def run(vd, *args, **kwargs):
|
85
|
-
vd.
|
137
|
+
vd.reloadMacros()
|
138
|
+
|
139
|
+
|
140
|
+
@VisiData.api
|
141
|
+
def reloadMacros(vd):
|
142
|
+
vd.macros.reload()
|
143
|
+
for r in vd.macros:
|
144
|
+
vs = vd.loadMacro(Path(r.source))
|
145
|
+
if vs:
|
146
|
+
vd.setMacro(r.binding, vs)
|
147
|
+
|
148
|
+
|
149
|
+
class MacrosGuide(GuideSheet):
|
150
|
+
guide_text = '''# Macros
|
151
|
+
Macros allow you to bind a command sequence to a keystroke or longname, to replay when that keystroke is pressed or the command is executed by longname.
|
152
|
+
|
153
|
+
The basic usage is:
|
154
|
+
1. {help.commands.macro_record}.
|
155
|
+
2. Execute a series of commands.
|
156
|
+
3. `m` again to complete the recording, and prompt for the keystroke or longname to bind it to.
|
157
|
+
|
158
|
+
The macro will then be executed everytime the provided keystroke or longname are used. Note: the Alt+keys and the function keys are left unbound; overriding other keys may conflict with existing bindings, now or in the future.
|
159
|
+
|
160
|
+
Executing a macro will the series of commands starting on the current row and column on the current sheet.
|
161
|
+
|
162
|
+
# The Macros Sheet
|
163
|
+
|
164
|
+
- {help.commands.macro_sheet}
|
165
|
+
|
166
|
+
- `d` (`delete-row`) to mark macros for deletion
|
167
|
+
- {help.commands.commit_sheet}
|
168
|
+
- `Enter` (`open-row`) to open the macro in the current row, and view the series of commands composing it'''
|
86
169
|
|
87
170
|
|
88
171
|
Sheet.addCommand('m', 'macro-record', 'vd.cmdlog.startMacro()', 'record macro')
|
89
|
-
Sheet.addCommand('gm', 'macro-sheet', 'vd.push(vd.macrosheet)', 'open macros
|
172
|
+
Sheet.addCommand('gm', 'macro-sheet', 'vd.push(vd.macrosheet)', 'open an index of existing macros')
|
173
|
+
|
174
|
+
vd.addMenuItems('''
|
175
|
+
System > Macros sheet > macro-sheet
|
176
|
+
''')
|
177
|
+
|
178
|
+
vd.addGuide('MacrosSheet', MacrosGuide)
|
visidata/main.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Usage: $0 [<options>] [<input> ...]
|
3
3
|
# $0 [<options>] --play <cmdlog> [--batch] [-w <waitsecs>] [-o <output>] [field=value ...]
|
4
4
|
|
5
|
-
__version__ = '
|
5
|
+
__version__ = '3.0'
|
6
6
|
__version_info__ = 'saul.pw/VisiData v' + __version__
|
7
7
|
|
8
8
|
from copy import copy
|
@@ -16,8 +16,6 @@ import signal
|
|
16
16
|
import warnings
|
17
17
|
import builtins # to override print
|
18
18
|
|
19
|
-
from pkg_resources import resource_filename
|
20
|
-
|
21
19
|
from visidata import vd, options, run, BaseSheet, AttrDict
|
22
20
|
from visidata import Path
|
23
21
|
from visidata.settings import _get_config_file
|
@@ -26,12 +24,13 @@ import visidata
|
|
26
24
|
vd.version_info = __version_info__
|
27
25
|
|
28
26
|
vd.option('config', _get_config_file(), 'config file to exec in Python', sheettype=None)
|
29
|
-
vd.option('play', '', 'file.
|
27
|
+
vd.option('play', '', 'file.vdj to replay')
|
30
28
|
vd.option('batch', False, 'replay in batch mode (with no interface and all status sent to stdout)')
|
31
29
|
vd.option('output', None, 'save the final visible sheet to output at the end of replay')
|
32
30
|
vd.option('preplay', '', 'longnames to preplay before replay')
|
33
31
|
vd.option('imports', 'plugins', 'imports to preload before .visidatarc (command-line only)')
|
34
32
|
vd.option('nothing', False, 'no config, no plugins, nothing extra')
|
33
|
+
vd.option('interactive', False, 'run interactive mode after batch replay')
|
35
34
|
|
36
35
|
# for --play
|
37
36
|
def eval_vd(logpath, *args, **kwargs):
|
@@ -57,7 +56,9 @@ def duptty():
|
|
57
56
|
try:
|
58
57
|
fin = open('/dev/tty')
|
59
58
|
fout = open('/dev/tty', mode='w')
|
60
|
-
stdin = open(os.dup(0),
|
59
|
+
stdin = open(os.dup(0),
|
60
|
+
encoding=vd.options.getonly('encoding', 'global', 'utf-8'),
|
61
|
+
errors=vd.options.getonly('encoding_errors', 'global', 'surrogateescape')) #2047
|
61
62
|
stdout = open(os.dup(1)) # for dumping to stdout from interface
|
62
63
|
os.dup2(fin.fileno(), 0)
|
63
64
|
os.dup2(fout.fileno(), 1)
|
@@ -71,24 +72,98 @@ def duptty():
|
|
71
72
|
|
72
73
|
return stdin, stdout
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
optalias('
|
80
|
-
optalias('
|
81
|
-
optalias('
|
82
|
-
optalias('
|
83
|
-
optalias('
|
84
|
-
optalias('
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
75
|
+
vd.optalias('i', 'interactive')
|
76
|
+
vd.optalias('N', 'nothing')
|
77
|
+
vd.optalias('f', 'filetype')
|
78
|
+
vd.optalias('p', 'play')
|
79
|
+
vd.optalias('b', 'batch')
|
80
|
+
vd.optalias('P', 'preplay')
|
81
|
+
vd.optalias('o', 'output')
|
82
|
+
vd.optalias('w', 'replay_wait')
|
83
|
+
vd.optalias('d', 'delimiter')
|
84
|
+
vd.optalias('c', 'config')
|
85
|
+
vd.optalias('r', 'dir_depth', 100000)
|
86
|
+
|
87
|
+
|
88
|
+
@visidata.VisiData.api
|
89
|
+
def parsePos(vd, arg:str, inputs=None):
|
90
|
+
'Return (startsheets:list, startrow:str, startcol:str) from *arg* like "+sheet:subsheet:col:row". Empty sheetstr in startsheets means the starting pos applies to all sheets.'
|
91
|
+
startsheets, startrow, startcol = [], None, None
|
91
92
|
|
93
|
+
if ':' not in arg:
|
94
|
+
return (None, arg, None)
|
95
|
+
|
96
|
+
pos = arg.split(':')
|
97
|
+
if len(pos) == 1:
|
98
|
+
startsheet = [Path(inputs[-1]).base_stem] if inputs else None
|
99
|
+
start_pos = (startsheet, pos[0], None)
|
100
|
+
elif len(pos) == 2:
|
101
|
+
startsheet = [Path(inputs[-1]).base_stem] if inputs else None
|
102
|
+
startrow, startcol = pos
|
103
|
+
start_pos = (None, startrow, startcol)
|
104
|
+
else: # if len(pos) >= 3:
|
105
|
+
startsheets = pos[:-2]
|
106
|
+
startrow, startcol = pos[-2:]
|
107
|
+
start_pos = (startsheets, startrow, startcol)
|
108
|
+
|
109
|
+
# index subsheets need to be loaded *after* the cursor indexing
|
110
|
+
vd.options.set('load_lazy', True, obj=start_pos[0])
|
111
|
+
|
112
|
+
return start_pos
|
113
|
+
|
114
|
+
|
115
|
+
@visidata.VisiData.api
|
116
|
+
def outputProgressEvery(vd, sheet, seconds:float=0.5):
|
117
|
+
import time
|
118
|
+
t0 = time.time()
|
119
|
+
while True:
|
120
|
+
time.sleep(seconds)
|
121
|
+
t = time.time()
|
122
|
+
print(f'\r[{t-t0:.1f}s] ', end='', file=sys.stderr)
|
123
|
+
if sheet:
|
124
|
+
print(f'{sheet.progressPct} ', end='', file=sys.stderr)
|
125
|
+
sys.stderr.flush()
|
126
|
+
|
127
|
+
@visidata.VisiData.api
|
128
|
+
def moveToPos(vd, sources, startsheets, startrow, startcol):
|
129
|
+
sheets = [] # sheets to apply startrow:startcol to
|
130
|
+
if not startsheets:
|
131
|
+
sheets = sources # apply row/col to all sheets
|
132
|
+
else:
|
133
|
+
startsheet = startsheets[0] or sources[-1]
|
134
|
+
vs = vd.getSheet(startsheet)
|
135
|
+
if not vs:
|
136
|
+
vd.warning(f'no sheet "{startsheet}"')
|
137
|
+
return
|
138
|
+
|
139
|
+
vd.sync(vs.ensureLoaded())
|
140
|
+
vd.clearCaches()
|
141
|
+
for startsheet in startsheets[1:]:
|
142
|
+
rowidx = vs.getRowIndexFromStr(vd.options.rowkey_prefix + startsheet)
|
143
|
+
if rowidx is None:
|
144
|
+
vd.warning(f'{vs.name} has no subsheet "{startsheet}"')
|
145
|
+
vs = None
|
146
|
+
break
|
147
|
+
vs = vs.rows[rowidx]
|
148
|
+
vd.sync(vs.ensureLoaded())
|
149
|
+
vd.clearCaches()
|
150
|
+
if vs:
|
151
|
+
vd.push(vs)
|
152
|
+
sheets = [vs]
|
153
|
+
|
154
|
+
if startrow:
|
155
|
+
for vs in sheets:
|
156
|
+
if vs:
|
157
|
+
vs.moveToRow(startrow) or vd.warning(f'{vs} has no row "{startrow}"')
|
158
|
+
|
159
|
+
if startcol:
|
160
|
+
for vs in sheets:
|
161
|
+
if vs:
|
162
|
+
if not vs.moveToCol(startcol):
|
163
|
+
if startcol.isdigit():
|
164
|
+
vs.moveToCol(int(startcol)) # handle indexing by column number
|
165
|
+
else:
|
166
|
+
vd.warning(f'{vs} has no column "{startcol}"')
|
92
167
|
|
93
168
|
def main_vd():
|
94
169
|
'Open the given sources using the VisiData interface.'
|
@@ -96,9 +171,9 @@ def main_vd():
|
|
96
171
|
print(vd.version_info)
|
97
172
|
return 0
|
98
173
|
if '-h' in sys.argv or '--help' in sys.argv:
|
99
|
-
|
100
|
-
print(fp.read())
|
174
|
+
print((Path(vd.pkg_resources_files(visidata)) / 'man' / 'vd.txt').open().read())
|
101
175
|
return 0
|
176
|
+
vd.status(__version_info__)
|
102
177
|
|
103
178
|
try:
|
104
179
|
locale.setlocale(locale.LC_ALL, '')
|
@@ -107,7 +182,6 @@ def main_vd():
|
|
107
182
|
|
108
183
|
warnings.showwarning = vd.warning
|
109
184
|
vd.printout = builtins.print
|
110
|
-
vd.printerr = lambda *args, **kwargs: builtins.print(*args, file=sys.stderr) # ignore kwargs (like priority)
|
111
185
|
|
112
186
|
flPipedInput = not sys.stdin.isatty()
|
113
187
|
flPipedOutput = not sys.stdout.isatty()
|
@@ -122,8 +196,7 @@ def main_vd():
|
|
122
196
|
vd.stdinSource = Path('-', fp=None) # fp filled in below after options parsed for encoding
|
123
197
|
|
124
198
|
# parse args, including +sheetname:subsheet:4:3 starting at row:col on sheetname:subsheet[:...]
|
125
|
-
|
126
|
-
startsheets, startrow, startcol = [], None, None
|
199
|
+
after_config = []
|
127
200
|
fmtargs = []
|
128
201
|
fmtkwargs = {}
|
129
202
|
inputs = []
|
@@ -158,10 +231,10 @@ def main_vd():
|
|
158
231
|
pass
|
159
232
|
|
160
233
|
optname = optname.replace('-', '_')
|
161
|
-
optname, optval =
|
234
|
+
optname, optval = vd._resolve_optalias(optname, optval)
|
162
235
|
|
163
|
-
if optval is None:
|
164
|
-
opt = options._get(optname)
|
236
|
+
if optval is None: # missing argument, maybe bool?
|
237
|
+
opt = vd.options._get(optname)
|
165
238
|
if opt:
|
166
239
|
if type(opt.value) is bool:
|
167
240
|
optval = True
|
@@ -177,24 +250,7 @@ def main_vd():
|
|
177
250
|
global_args[optname] = optval
|
178
251
|
|
179
252
|
elif arg.startswith('+'): # position cursor at start
|
180
|
-
|
181
|
-
pos = arg[1:].split(':')
|
182
|
-
if len(pos) == 1:
|
183
|
-
startsheet = [Path(inputs[-1]).name] if inputs else None
|
184
|
-
start_positions.append((startsheet, pos[0], None))
|
185
|
-
elif len(pos) == 2:
|
186
|
-
startsheet = [Path(inputs[-1]).name] if inputs else None
|
187
|
-
startrow, startcol = pos
|
188
|
-
start_positions.append((None, startrow, startcol))
|
189
|
-
elif len(pos) >= 3:
|
190
|
-
startsheets = pos[:-2]
|
191
|
-
startrow, startcol = pos[-2:]
|
192
|
-
start_positions.append((startsheets, startrow, startcol))
|
193
|
-
if start_positions[-1]:
|
194
|
-
# index subsheets need to be loaded *after* the cursor indexing
|
195
|
-
options.set('load_lazy', True, obj=start_positions[-1][0])
|
196
|
-
else:
|
197
|
-
start_positions.append((None, arg[1:], None))
|
253
|
+
after_config.append((vd.moveToPos, *vd.parsePos(arg[1:], inputs=inputs)))
|
198
254
|
|
199
255
|
elif current_args.get('play', None) and '=' in arg:
|
200
256
|
# parse 'key=value' pairs for formatting cmdlog template in replay mode
|
@@ -216,17 +272,16 @@ def main_vd():
|
|
216
272
|
|
217
273
|
vd._stdin, vd._stdout = duptty() # always dup stdin/stdout
|
218
274
|
vd.stdinSource.fptext = vd._stdin
|
275
|
+
vd._stdin.close = vd.nop #1759
|
219
276
|
|
220
|
-
# fetch motd
|
221
|
-
vd.pluginsSheet.ensureLoaded()
|
277
|
+
# fetch motd *after* options parsing/setting
|
222
278
|
vd.domotd()
|
223
279
|
|
224
280
|
if args.batch:
|
225
281
|
options.undo = False
|
226
282
|
options.quitguard = False
|
227
|
-
vd.status = vd.printerr
|
228
283
|
vd.editline = lambda *args, **kwargs: ''
|
229
|
-
vd.execAsync =
|
284
|
+
vd.execAsync = vd.execSync # disable async
|
230
285
|
|
231
286
|
for cmd in (args.preplay or '').split():
|
232
287
|
BaseSheet('').execCommand(cmd)
|
@@ -275,45 +330,8 @@ def main_vd():
|
|
275
330
|
vd.push(sources[0])
|
276
331
|
sources[0].reload()
|
277
332
|
|
278
|
-
for
|
279
|
-
|
280
|
-
if not startsheets:
|
281
|
-
sheets = sources # apply row/col to all sheets
|
282
|
-
else:
|
283
|
-
startsheet = startsheets[0] or sources[-1]
|
284
|
-
vs = vd.getSheet(startsheet)
|
285
|
-
if not vs:
|
286
|
-
vd.warning(f'no sheet "{startsheet}"')
|
287
|
-
continue
|
288
|
-
|
289
|
-
vd.sync(vs.ensureLoaded())
|
290
|
-
vd.clearCaches()
|
291
|
-
for startsheet in startsheets[1:]:
|
292
|
-
rowidx = vs.getRowIndexFromStr(options.rowkey_prefix + startsheet)
|
293
|
-
if rowidx is None:
|
294
|
-
vd.warning(f'{vs.name} has no subsheet "{startsheet}"')
|
295
|
-
vs = None
|
296
|
-
break
|
297
|
-
vs = vs.rows[rowidx]
|
298
|
-
vd.sync(vs.ensureLoaded())
|
299
|
-
vd.clearCaches()
|
300
|
-
if vs:
|
301
|
-
vd.push(vs)
|
302
|
-
sheets = [vs]
|
303
|
-
|
304
|
-
if startrow:
|
305
|
-
for vs in sheets:
|
306
|
-
if vs:
|
307
|
-
vs.moveToRow(startrow) or vd.warning(f'{vs} has no row "{startrow}"')
|
308
|
-
|
309
|
-
if startcol:
|
310
|
-
for vs in sheets:
|
311
|
-
if vs:
|
312
|
-
if not vs.moveToCol(startcol):
|
313
|
-
if startcol.isdigit():
|
314
|
-
vs.moveToCol(int(startcol)) # handle indexing by column number
|
315
|
-
else:
|
316
|
-
vd.warning(f'{vs} has no column "{startcol}"')
|
333
|
+
for (f, *parms) in after_config:
|
334
|
+
f(sources, *parms)
|
317
335
|
|
318
336
|
if not args.batch:
|
319
337
|
run(vd.sheets[0])
|
@@ -327,10 +345,16 @@ def main_vd():
|
|
327
345
|
vs = eval_vd(vdfile, *fmtargs, **fmtkwargs)
|
328
346
|
vd.sync(vs.reload())
|
329
347
|
if args.batch:
|
348
|
+
if not args.debug:
|
349
|
+
vd.outputProgressThread = visidata.VisiData.execAsync(vd, vd.outputProgressEvery, vs, seconds=0.5, sheet=BaseSheet()) #1182
|
330
350
|
if vd.replay_sync(vs): # error
|
331
351
|
return 1
|
352
|
+
|
353
|
+
if vd.options.interactive:
|
354
|
+
vd.editline = lambda *args, vd=vd, **kwargs: visidata.VisiData.editline(vd, *args, **kwargs)
|
355
|
+
vd.execAsync = lambda *args, vd=vd, **kwargs: visidata.VisiData.execAsync(vd, *args, **kwargs)
|
356
|
+
run()
|
332
357
|
else:
|
333
|
-
vd.currentReplay = vs
|
334
358
|
vd.replay(vs)
|
335
359
|
run()
|
336
360
|
|
@@ -348,18 +372,19 @@ def main_vd():
|
|
348
372
|
return 0
|
349
373
|
|
350
374
|
def vd_cli():
|
351
|
-
vd.status(__version_info__)
|
352
375
|
rc = -1
|
353
376
|
try:
|
354
377
|
rc = main_vd()
|
355
378
|
except BrokenPipeError:
|
356
379
|
os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) # handle broken pipe gracefully
|
357
380
|
except visidata.ExpectedException as e:
|
358
|
-
|
381
|
+
if vd.options.debug:
|
382
|
+
raise
|
359
383
|
except FileNotFoundError as e:
|
360
384
|
print(e)
|
361
385
|
if options.debug:
|
362
386
|
raise
|
387
|
+
|
363
388
|
sys.stderr.flush()
|
364
389
|
sys.stdout.flush()
|
365
390
|
os._exit(rc) # cleanup can be expensive with large datasets
|