visidata 3.1.1__py3-none-any.whl → 3.3__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 +2 -2
- visidata/_input.py +106 -58
- visidata/_open.py +10 -7
- visidata/_types.py +2 -2
- visidata/aggregators.py +125 -16
- visidata/apps/vdsql/_ibis.py +8 -13
- visidata/basesheet.py +4 -3
- visidata/canvas.py +11 -7
- visidata/clipboard.py +11 -2
- visidata/cliptext.py +68 -23
- visidata/cmdlog.py +5 -1
- visidata/column.py +48 -33
- visidata/ddwplay.py +2 -2
- visidata/deprecated.py +96 -63
- visidata/errors.py +41 -5
- visidata/{features → experimental}/helloworld.py +1 -1
- visidata/experimental/liveupdate.py +1 -1
- visidata/expr.py +1 -0
- visidata/extensible.py +4 -0
- visidata/features/cmdpalette.py +64 -25
- visidata/features/describe.py +2 -2
- visidata/features/expand_cols.py +7 -5
- visidata/features/freeze.py +14 -2
- visidata/features/go_col.py +3 -3
- visidata/features/graph_zoom_y.py +47 -0
- visidata/features/incr.py +7 -3
- visidata/features/join.py +23 -12
- visidata/features/layout.py +8 -4
- visidata/features/melt.py +1 -0
- visidata/features/rank.py +103 -0
- visidata/features/reload_every.py +11 -8
- visidata/features/sysedit.py +14 -4
- visidata/features/transpose.py +1 -0
- visidata/features/window.py +12 -0
- visidata/form.py +10 -9
- visidata/freqtbl.py +47 -3
- visidata/fuzzymatch.py +11 -7
- visidata/graph.py +5 -3
- visidata/guides/AggregatorsSheet.md +84 -0
- visidata/guides/CommandsSheet.md +1 -0
- visidata/guides/MacrosSheet.md +1 -1
- visidata/guides/RankGuide.md +51 -0
- visidata/guides/TypesSheet.md +1 -1
- visidata/guides/WindowFunctionGuide.md +49 -0
- visidata/help.py +23 -6
- visidata/indexsheet.py +1 -1
- visidata/loaders/_pandas.py +3 -1
- visidata/loaders/archive.py +33 -6
- visidata/loaders/csv.py +12 -1
- visidata/loaders/eml.py +2 -0
- visidata/loaders/f5log.py +2 -2
- visidata/loaders/fec.py +6 -9
- visidata/loaders/fixed_width.py +2 -0
- visidata/loaders/hdf5.py +34 -10
- visidata/loaders/npy.py +54 -23
- visidata/loaders/orgmode.py +3 -2
- visidata/loaders/pandas_freqtbl.py +4 -0
- visidata/loaders/psv.py +13 -0
- visidata/loaders/sqlite.py +1 -1
- visidata/loaders/vds.py +3 -4
- visidata/macros.py +5 -4
- visidata/main.py +21 -11
- visidata/mainloop.py +8 -5
- visidata/man/parse_options.py +3 -2
- visidata/man/vd.1 +38 -17
- visidata/man/vd.txt +47 -17
- visidata/menu.py +10 -10
- visidata/metasheets.py +3 -3
- visidata/mouse.py +3 -0
- visidata/movement.py +6 -3
- visidata/pyobj.py +17 -9
- visidata/save.py +10 -2
- visidata/selection.py +29 -18
- visidata/settings.py +9 -5
- visidata/sheets.py +124 -48
- visidata/shell.py +2 -2
- visidata/sidebar.py +11 -8
- visidata/sort.py +89 -11
- visidata/statusbar.py +10 -9
- visidata/tests/test_cliptext.py +164 -0
- visidata/tests/test_commands.py +6 -2
- visidata/tests/test_menu.py +1 -1
- visidata/textsheet.py +34 -8
- visidata/themes/ascii8.py +2 -2
- visidata/themes/light.py +5 -0
- visidata/threads.py +38 -8
- visidata/utils.py +15 -1
- visidata/vendor/__init__.py +0 -0
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/man/man1/vd.1 +38 -17
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/man/man1/visidata.1 +38 -17
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/METADATA +62 -15
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/RECORD +98 -92
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/WHEEL +1 -1
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/entry_points.txt +1 -0
- visidata-3.1.1.data/scripts/vd +0 -6
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/applications/visidata.desktop +0 -0
- {visidata-3.1.1.data → visidata-3.3.data}/scripts/vd2to3.vdx +0 -0
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/top_level.txt +0 -0
visidata/man/vd.txt
CHANGED
@@ -108,6 +108,8 @@ DESCRIPTION
|
|
108
108
|
gz_ number adjust widths of all visible columns to Ar number
|
109
109
|
|
110
110
|
- (hyphen) hide current column
|
111
|
+
g- (hyphen) hide any column that has multiple rows but only one dis‐
|
112
|
+
tinct value
|
111
113
|
z- reduce width of current column by half
|
112
114
|
gv unhide all columns
|
113
115
|
|
@@ -152,11 +154,12 @@ DESCRIPTION
|
|
152
154
|
requires example row)
|
153
155
|
z; expr create new column from bash expr, with $columnNames as
|
154
156
|
variables
|
155
|
-
*
|
156
|
-
|
157
|
-
|
157
|
+
* search Tab replace
|
158
|
+
add column derived from current column, replacing search
|
159
|
+
regex with replace (may include \1 backrefs)
|
160
|
+
g* gz* search Tab replace
|
158
161
|
modify selected rows in current/all visible column(s),
|
159
|
-
replacing
|
162
|
+
replacing search with replace (may include \1 backrefs)
|
160
163
|
|
161
164
|
( g( expand current/all visible column(s) of lists (e.g. [3])
|
162
165
|
or dicts (e.g. {3}) one level
|
@@ -191,8 +194,8 @@ DESCRIPTION
|
|
191
194
|
existing sort criteria
|
192
195
|
g[ g] sort ascending/descending by all key columns; replace
|
193
196
|
any existing sort criteria
|
194
|
-
z[ z] sort ascending/descending by current column;
|
195
|
-
|
197
|
+
z[ z] sort ascending/descending by current column; keep higher
|
198
|
+
priority sort criteria
|
196
199
|
gz[ gz] sort ascending/descending by all key columns; add to ex‐
|
197
200
|
isting sort criteria
|
198
201
|
" open duplicate sheet with only selected rows
|
@@ -202,7 +205,8 @@ DESCRIPTION
|
|
202
205
|
The rows in these duplicated sheets (except deepcopy) are references to
|
203
206
|
rows on the original source sheets, and so edits to the filtered rows
|
204
207
|
will naturally be reflected in the original rows. Use g' to freeze sheet
|
205
|
-
contents in a deliberate copy.
|
208
|
+
contents in a deliberate copy. z' replace current column with a frozen
|
209
|
+
copy, with all cells evaluated
|
206
210
|
|
207
211
|
Editing Rows and Cells
|
208
212
|
a za append blank row/column; appended columns cannot be
|
@@ -232,6 +236,7 @@ DESCRIPTION
|
|
232
236
|
f fill null cells in current column with contents of non-
|
233
237
|
null cells up the current column
|
234
238
|
e text edit contents of current cell
|
239
|
+
^O edit contents of current cell in external EDITOR
|
235
240
|
ge text set contents of current column for selected rows to text
|
236
241
|
|
237
242
|
Commands While Editing Input
|
@@ -292,6 +297,7 @@ DESCRIPTION
|
|
292
297
|
+ - increase/decrease zoom level, centered on cursor
|
293
298
|
_ (underbar) zoom to fit full extent
|
294
299
|
z_ (underbar) set aspect ratio
|
300
|
+
g_ (underbar) Zoom y-axis to fit all visible data points
|
295
301
|
x xmin xmax set xmin/xmax on graph
|
296
302
|
y ymin ymax set ymin/ymax on graph
|
297
303
|
s t u select/toggle/unselect rows on source sheet con‐
|
@@ -582,7 +588,8 @@ COMMANDLINE OPTIONS
|
|
582
588
|
files
|
583
589
|
-N, --nothing=T False disable loading
|
584
590
|
.visidatarc and plugin addons
|
585
|
-
--visidata-dir=str
|
591
|
+
--visidata-dir=str /home/anja/.config/visidata
|
592
|
+
directory to load and
|
586
593
|
store additional files
|
587
594
|
--debug False exit on error and display
|
588
595
|
stacktrace
|
@@ -702,7 +709,7 @@ COMMANDLINE OPTIONS
|
|
702
709
|
DirSheet
|
703
710
|
--dir-hidden False load hidden files on
|
704
711
|
DirSheet
|
705
|
-
--config=Path /home/
|
712
|
+
--config=Path /home/anja/.visidatarc
|
706
713
|
config file to exec in
|
707
714
|
Python
|
708
715
|
--play=str file.vdj to replay
|
@@ -739,7 +746,7 @@ COMMANDLINE OPTIONS
|
|
739
746
|
--reddit-client-id=str client_id for reddit api
|
740
747
|
--reddit-client-secret=str client_secret for reddit
|
741
748
|
api
|
742
|
-
--reddit-user-agent=str 3.
|
749
|
+
--reddit-user-agent=str 3.3 user_agent for reddit api
|
743
750
|
--zulip-batch-size=int -100 number of messages to
|
744
751
|
fetch per call (<0 to
|
745
752
|
fetch before anchor)
|
@@ -799,6 +806,8 @@ COMMANDLINE OPTIONS
|
|
799
806
|
--grep-base-dir=NoneType None base directory for rela‐
|
800
807
|
tive paths opened with
|
801
808
|
sysopen-row
|
809
|
+
--hdf5-matrix-enumerate False enumerate matrix rows and
|
810
|
+
columns
|
802
811
|
--html-title=str <h2>{sheet.name}</h2>
|
803
812
|
table header when saving
|
804
813
|
to html
|
@@ -810,6 +819,8 @@ COMMANDLINE OPTIONS
|
|
810
819
|
cates for https
|
811
820
|
--npy-allow-pickle False numpy allow unpickling
|
812
821
|
objects (unsafe)
|
822
|
+
--npy-matrix-enumerate False enumerate matrix rows and
|
823
|
+
columns
|
813
824
|
--pcap-internet=str n (y/s/n) if save_dot in‐
|
814
825
|
cludes all internet hosts
|
815
826
|
separately (y), combined
|
@@ -851,8 +862,6 @@ COMMANDLINE OPTIONS
|
|
851
862
|
--describe-aggrs=str mean stdev numeric aggregators to
|
852
863
|
calculate on Describe
|
853
864
|
sheet
|
854
|
-
--hello-world=str ¡Hola mundo! shown by the hello-world
|
855
|
-
command
|
856
865
|
--incr-base=float 1.0 start value for column
|
857
866
|
increments
|
858
867
|
--ping-count=int 3 send this many pings to
|
@@ -897,7 +906,7 @@ COMMANDLINE OPTIONS
|
|
897
906
|
onto sheet stack
|
898
907
|
disp_menu_input … indicator if input required for
|
899
908
|
command
|
900
|
-
disp_menu_fmt | VisiData {vd.version} | {vd.
|
909
|
+
disp_menu_fmt | VisiData {vd.version} | {vd.motd}
|
901
910
|
right-side menu format string
|
902
911
|
disp_float_fmt {:.02f} default fmtstr to format float
|
903
912
|
values
|
@@ -984,6 +993,8 @@ COMMANDLINE OPTIONS
|
|
984
993
|
disp_wrap_placeholder … multiline string to indicate
|
985
994
|
truncation
|
986
995
|
disp_multiline_focus True only multiline cursor row
|
996
|
+
color_multiline_bottom color of bottom line of multiline
|
997
|
+
rows
|
987
998
|
color_aggregator bold 255 white on 234 black
|
988
999
|
color of aggregator summary on
|
989
1000
|
bottom row
|
@@ -1033,7 +1044,7 @@ COMMANDLINE OPTIONS
|
|
1033
1044
|
⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿
|
1034
1045
|
charset to render 2x4 blocks on
|
1035
1046
|
canvas
|
1036
|
-
|
1047
|
+
disp_graph_pixel_random False randomly choose attr from set of
|
1037
1048
|
pixels instead of most common
|
1038
1049
|
disp_zoom_incr 2.0 amount to multiply current zoom‐
|
1039
1050
|
level when zooming
|
@@ -1050,8 +1061,27 @@ COMMANDLINE OPTIONS
|
|
1050
1061
|
erence lines on graph
|
1051
1062
|
disp_graph_multiple_reflines_char ▒ char to render multiple parallel
|
1052
1063
|
reflines
|
1053
|
-
|
1054
|
-
|
1064
|
+
disp_help_flags cmdpalette guides help hints inputfield inputkeys
|
1065
|
+
nometacols sidebar
|
1066
|
+
list of helper features to enable
|
1067
|
+
(space-separated):
|
1068
|
+
- "cmdpalette": exec-longname
|
1069
|
+
suggestions
|
1070
|
+
- "guides": guides in sidebar
|
1071
|
+
- "help": help sidebar col‐
|
1072
|
+
lapsed by default
|
1073
|
+
- "hints": context-sensitive
|
1074
|
+
hints on menu line
|
1075
|
+
- "inputfield": context-sen‐
|
1076
|
+
sitive help for each input field
|
1077
|
+
- "inputkeys": input quick
|
1078
|
+
reference in sidebar
|
1079
|
+
- "nometacols": hide expert
|
1080
|
+
columns on metasheets
|
1081
|
+
- "sidebar": context-sensi‐
|
1082
|
+
tive sheet help in sidebar
|
1083
|
+
- "all": enable all helper
|
1084
|
+
features
|
1055
1085
|
color_add_pending green color for rows pending add
|
1056
1086
|
color_change_pending reverse yellow color for cells pending modifica‐
|
1057
1087
|
tion
|
@@ -1189,4 +1219,4 @@ SUPPORTED SOURCES
|
|
1189
1219
|
AUTHOR
|
1190
1220
|
VisiData was made by Saul Pwanson <vd@saul.pw>.
|
1191
1221
|
|
1192
|
-
Linux/MacOS
|
1222
|
+
Linux/MacOS June 13, 2025 Linux/MacOS
|
visidata/menu.py
CHANGED
@@ -18,14 +18,14 @@ vd.theme_option('disp_menu_boxchars', '││──┌┐└┘├┤', 'box cha
|
|
18
18
|
vd.theme_option('disp_menu_more', '»', 'command submenu indicator')
|
19
19
|
vd.theme_option('disp_menu_push', '⎘', 'indicator if command pushes sheet onto sheet stack')
|
20
20
|
vd.theme_option('disp_menu_input', '…', 'indicator if input required for command')
|
21
|
-
vd.option('disp_menu_fmt', '| VisiData {vd.version} | {vd.
|
21
|
+
vd.option('disp_menu_fmt', '| VisiData {vd.version} | {vd.motd}', 'right-side menu format string')
|
22
22
|
|
23
23
|
BaseSheet.init('activeMenuItems', list)
|
24
24
|
vd.menuRunning = False
|
25
25
|
|
26
26
|
@VisiData.property
|
27
27
|
def hintStatus(vd):
|
28
|
-
if vd.
|
28
|
+
if vd.wantsHelp('hints'):
|
29
29
|
if int(time.time()/60) % 2 == 0:
|
30
30
|
return 'Alt+H for help menu'
|
31
31
|
else:
|
@@ -178,9 +178,9 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
|
|
178
178
|
|
179
179
|
maxbinding = 0
|
180
180
|
if vd.options.disp_menu_keys:
|
181
|
-
maxbinding = max(
|
181
|
+
maxbinding = max(dispwidth(item.binding or '') for item in menus)+1
|
182
182
|
|
183
|
-
w = max(
|
183
|
+
w = max(dispwidth(item.title) for item in menus)+maxbinding+2
|
184
184
|
|
185
185
|
# draw borders before/under submenus
|
186
186
|
if level > 1:
|
@@ -225,11 +225,11 @@ def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
|
|
225
225
|
mainbinding = vd.prettykeys(revbinds[0])
|
226
226
|
|
227
227
|
# actually display the menu item
|
228
|
-
title += ' '*(w-
|
228
|
+
title += ' '*(w-dispwidth(pretitle)-dispwidth(item.title)+1) # padding
|
229
229
|
|
230
230
|
menudraw(scr, y+i, x+1, pretitle+title, attr)
|
231
231
|
if maxbinding and mainbinding:
|
232
|
-
menudraw(scr, y+i, x+1+w-
|
232
|
+
menudraw(scr, y+i, x+1+w-dispwidth(mainbinding), mainbinding, attr.update(colors.keystrokes))
|
233
233
|
menudraw(scr, y+i, x+2+w, titlenote, attr)
|
234
234
|
menudraw(scr, y+i, x+3+w, ls, colors.color_menu)
|
235
235
|
|
@@ -317,7 +317,7 @@ def drawMenu(vd, scr, sheet):
|
|
317
317
|
BUTTON1_RELEASED=vd.nop,
|
318
318
|
BUTTON2_RELEASED=vd.nop,
|
319
319
|
BUTTON3_RELEASED=vd.nop)
|
320
|
-
x +=
|
320
|
+
x += dispwidth(item.title)+2
|
321
321
|
|
322
322
|
rightdisp = sheet.options.disp_menu_fmt.format(sheet=sheet, vd=vd)
|
323
323
|
menudraw(scr, 0, x+4, rightdisp, colors.color_menu)
|
@@ -355,13 +355,13 @@ def drawMenu(vd, scr, sheet):
|
|
355
355
|
|
356
356
|
# cmd.helpstr text
|
357
357
|
for i, line in enumerate(helplines):
|
358
|
-
menudraw(scr, y+i, helpx, ls+' '+line+' '*(helpw-
|
358
|
+
menudraw(scr, y+i, helpx, ls+' '+line+' '*(helpw-dispwidth(line)-3)+rs, helpattr)
|
359
359
|
y += len(helplines)
|
360
360
|
|
361
361
|
if sidelines:
|
362
362
|
menudraw(scr, y, helpx, ls+' '*(helpw-2)+rs, helpattr)
|
363
363
|
for i, line in enumerate(sidelines):
|
364
|
-
menudraw(scr, y+i+1, helpx, ls+' '+line+' '*(helpw-
|
364
|
+
menudraw(scr, y+i+1, helpx, ls+' '+line+' '*(helpw-dispwidth(line)-6)+rs, helpattr)
|
365
365
|
y += len(sidelines)+1
|
366
366
|
|
367
367
|
menudraw(scr, y, helpx, bl+bs*(helpw-2)+br, helpattr)
|
@@ -371,7 +371,7 @@ def drawMenu(vd, scr, sheet):
|
|
371
371
|
menudraw(scr, menuy, helpx+2, rsl, helpattr)
|
372
372
|
ks = vd.prettykeys(mainbinding or '(unbound)')
|
373
373
|
menudraw(scr, menuy, helpx+3, ' '+ks+' ', colors.color_menu_active)
|
374
|
-
menudraw(scr, menuy, helpx+2+
|
374
|
+
menudraw(scr, menuy, helpx+2+dispwidth(ks)+3, lsr, helpattr)
|
375
375
|
menudraw(scr, menuy, helpx+19, ' '+cmd.longname+' ', helpattr)
|
376
376
|
|
377
377
|
vd.onMouse(scr, helpx, menuy, helpw, y-menuy+1,
|
visidata/metasheets.py
CHANGED
@@ -38,8 +38,8 @@ Other commands (not specific to Columns Sheet):
|
|
38
38
|
'passthrough to the value on the source cursorRow'
|
39
39
|
def calcValue(self, srcCol):
|
40
40
|
return srcCol.getDisplayValue(srcCol.sheet.cursorRow)
|
41
|
-
def setValue(self, srcCol, val):
|
42
|
-
srcCol.setValue(srcCol.sheet.cursorRow, val)
|
41
|
+
def setValue(self, srcCol, val, setModified=True):
|
42
|
+
srcCol.setValue(srcCol.sheet.cursorRow, val, setModified=setModified)
|
43
43
|
|
44
44
|
columns = [
|
45
45
|
ColumnAttr('sheet', type=str),
|
@@ -134,7 +134,7 @@ globalCommand('gC', 'columns-all', 'vd.push(vd.allColumnsSheet)', 'open Columns
|
|
134
134
|
Sheet.addCommand('C', 'columns-sheet', 'vd.push(ColumnsSheet(name+"_columns", source=[sheet]))', 'open Columns Sheet: edit column properties for current sheet')
|
135
135
|
|
136
136
|
# used ColumnsSheet, affecting the 'row' (source column)
|
137
|
-
ColumnsSheet.addCommand('g!', 'key-selected', 'for c in onlySelectedRows: c.sheet.setKeys([c])', '
|
137
|
+
ColumnsSheet.addCommand('g!', 'key-selected', 'for c in onlySelectedRows: c.sheet.setKeys([c])', 'set selected source columns as key columns')
|
138
138
|
ColumnsSheet.addCommand('gz!', 'key-off-selected', 'for c in onlySelectedRows: c.sheet.unsetKeys([c])', 'unset selected source columns as key columns')
|
139
139
|
|
140
140
|
ColumnsSheet.addCommand('g-', 'hide-selected', 'onlySelectedRows.hide()', 'hide selected source columns')
|
visidata/mouse.py
CHANGED
@@ -13,8 +13,11 @@ BaseSheet.init('mouseY', int)
|
|
13
13
|
|
14
14
|
@VisiData.after
|
15
15
|
def initCurses(vd):
|
16
|
+
if not getattr(curses, 'mousemask', None):
|
17
|
+
return
|
16
18
|
curses.MOUSE_ALL = 0xffffffff
|
17
19
|
curses.mousemask(curses.MOUSE_ALL if vd.options.mouse_interval else 0)
|
20
|
+
curses.def_prog_mode()
|
18
21
|
curses.mouseinterval(vd.options.mouse_interval)
|
19
22
|
curses.mouseEvents = {}
|
20
23
|
|
visidata/movement.py
CHANGED
@@ -84,9 +84,12 @@ def moveToNextRow(vs, func, reverse=False, msg='no different value up this colum
|
|
84
84
|
def visibleWidth(self):
|
85
85
|
'Width of column as is displayed in terminal'
|
86
86
|
vcolidx = self.sheet.visibleCols.index(self)
|
87
|
-
if vcolidx
|
88
|
-
self.sheet.
|
89
|
-
|
87
|
+
if vcolidx in self.sheet._visibleColLayout:
|
88
|
+
w = self.sheet._visibleColLayout[vcolidx][1]
|
89
|
+
else: #this case should never happen in normal use
|
90
|
+
#the width can be inaccurate if the column is not at x=0
|
91
|
+
w = self.sheet.calcSingleColLayout(vcolidx)
|
92
|
+
return w
|
90
93
|
|
91
94
|
|
92
95
|
Sheet.addCommand(None, 'go-left', 'cursorRight(-1)', 'go left', replay=False)
|
visidata/pyobj.py
CHANGED
@@ -3,7 +3,7 @@ import inspect
|
|
3
3
|
import math
|
4
4
|
import numbers
|
5
5
|
|
6
|
-
from visidata import vd, asyncthread, ENTER, deduceType
|
6
|
+
from visidata import vd, asyncthread, ENTER, deduceType, anytype
|
7
7
|
from visidata import Sheet, Column, VisiData, ColumnItem, TableSheet, BaseSheet, Progress, ColumnAttr, SuspendCurses, TextSheet, setitem
|
8
8
|
import visidata
|
9
9
|
|
@@ -46,14 +46,19 @@ def view(vd, obj):
|
|
46
46
|
vd.run(PyobjSheet(getattr(obj, '__name__', ''), source=obj))
|
47
47
|
|
48
48
|
|
49
|
-
|
50
|
-
def getPublicAttrs(obj):
|
51
|
-
'Return all public attributes (not methods or `_`-prefixed) on object.'
|
52
|
-
return [k for k in dir(obj) if not k.startswith('_') and not callable(getattr(obj, k))]
|
53
|
-
|
54
49
|
def PyobjColumns(obj):
|
55
50
|
'Return columns for each public attribute on an object.'
|
56
|
-
|
51
|
+
cols = []
|
52
|
+
for k in dir(obj):
|
53
|
+
coltype = anytype
|
54
|
+
try:
|
55
|
+
if k.startswith('_') or callable(getattr(obj, k)):
|
56
|
+
continue
|
57
|
+
coltype = deduceType(getattr(obj, k))
|
58
|
+
except AttributeError: #2631 attributes like formatted_help can raise AttributeError
|
59
|
+
pass
|
60
|
+
cols.append(ColumnAttr(k, type=coltype))
|
61
|
+
return cols
|
57
62
|
|
58
63
|
def AttrColumns(attrnames):
|
59
64
|
'Return column names for all elements of list `attrnames`.'
|
@@ -151,8 +156,11 @@ class ColumnSourceAttr(Column):
|
|
151
156
|
'Use row as attribute name on sheet source'
|
152
157
|
def calcValue(self, attrname):
|
153
158
|
return getattr(self.sheet.source, attrname)
|
154
|
-
def setValue(self, attrname, value):
|
155
|
-
|
159
|
+
def setValue(self, attrname, value, setModified=True):
|
160
|
+
ret = setattr(self.sheet.source, attrname, value)
|
161
|
+
if setModified:
|
162
|
+
self.sheet.setModified()
|
163
|
+
return ret
|
156
164
|
|
157
165
|
def docstring(obj, attr):
|
158
166
|
v = getattr(obj, attr)
|
visidata/save.py
CHANGED
@@ -85,6 +85,8 @@ def getDefaultSaveName(sheet):
|
|
85
85
|
if hasattr(src, 'scheme') and src.scheme:
|
86
86
|
return src.name + src.suffix
|
87
87
|
if isinstance(src, Path):
|
88
|
+
if src.given == '-':
|
89
|
+
return f'stdin.{sheet.options.save_filetype}'
|
88
90
|
if sheet.options.is_set('save_filetype', sheet):
|
89
91
|
# if save_filetype is over-ridden from default, use it as the extension
|
90
92
|
return str(src.with_suffix('')) + '.' + sheet.options.save_filetype
|
@@ -109,11 +111,17 @@ def saveCols(vd, cols):
|
|
109
111
|
|
110
112
|
@VisiData.api
|
111
113
|
def saveSheets(vd, givenpath, *vsheets, confirm_overwrite=True):
|
112
|
-
'Save all *vsheets* to *givenpath*.
|
114
|
+
'''Save all *vsheets* to *givenpath*. Async.
|
115
|
+
Callers should be careful not to call reload() while saveSheets is still running.
|
116
|
+
Use vd.sync(saveSheets) to wait for the save to finish.'''
|
113
117
|
|
114
118
|
if not vsheets: # blank tuple
|
115
119
|
vd.warning('no sheets to save')
|
116
120
|
return
|
121
|
+
if not givenpath.name:
|
122
|
+
vd.warning('no save path given')
|
123
|
+
return
|
124
|
+
|
117
125
|
unloaded = [ vs for vs in vsheets if vs.rows is UNLOADED ]
|
118
126
|
vd.sync(*vd.ensureLoaded(unloaded))
|
119
127
|
|
@@ -128,7 +136,7 @@ def saveSheets(vd, givenpath, *vsheets, confirm_overwrite=True):
|
|
128
136
|
break
|
129
137
|
|
130
138
|
if savefunc is None:
|
131
|
-
vd.fail(f'no function to save as {
|
139
|
+
vd.fail(f'no function to save as {", ".join(filetypes)}')
|
132
140
|
|
133
141
|
if confirm_overwrite:
|
134
142
|
vd.confirmOverwrite(givenpath)
|
visidata/selection.py
CHANGED
@@ -20,9 +20,10 @@ def isSelected(self, row):
|
|
20
20
|
|
21
21
|
@Sheet.api
|
22
22
|
@asyncthread
|
23
|
-
def toggle(self, rows):
|
23
|
+
def toggle(self, rows, add_undo=True):
|
24
24
|
'Toggle selection of given *rows*. Async.'
|
25
|
-
|
25
|
+
if add_undo:
|
26
|
+
self.addUndoSelection()
|
26
27
|
for r in Progress(rows, 'toggling', total=len(rows)):
|
27
28
|
if self.isSelected(r): #1671
|
28
29
|
self.unselectRow(r)
|
@@ -30,17 +31,24 @@ def toggle(self, rows):
|
|
30
31
|
self.selectRow(r)
|
31
32
|
|
32
33
|
|
34
|
+
@Sheet.before
|
35
|
+
def beforeLoad(self):
|
36
|
+
self._selectedRows.clear()
|
37
|
+
|
38
|
+
|
33
39
|
@Sheet.api
|
34
|
-
def select_row(self, row):
|
40
|
+
def select_row(self, row, add_undo=True):
|
35
41
|
'Add single *row* to set of selected rows.'
|
36
|
-
|
42
|
+
if add_undo:
|
43
|
+
self.addUndoSelection()
|
37
44
|
self.selectRow(row)
|
38
45
|
|
39
46
|
|
40
47
|
@Sheet.api
|
41
|
-
def toggle_row(self, row):
|
48
|
+
def toggle_row(self, row, add_undo=True):
|
42
49
|
'Toggle selection of given *row*.'
|
43
|
-
|
50
|
+
if add_undo:
|
51
|
+
self.addUndoSelection()
|
44
52
|
if self.isSelected(row):
|
45
53
|
self.unselectRow(row)
|
46
54
|
else:
|
@@ -48,9 +56,10 @@ def toggle_row(self, row):
|
|
48
56
|
|
49
57
|
|
50
58
|
@Sheet.api
|
51
|
-
def unselect_row(self, row):
|
59
|
+
def unselect_row(self, row, add_undo=True):
|
52
60
|
'Remove single *row* from set of selected rows.'
|
53
|
-
|
61
|
+
if add_undo:
|
62
|
+
self.addUndoSelection()
|
54
63
|
self.unselectRow(row) or vd.warning('row not selected')
|
55
64
|
|
56
65
|
|
@@ -77,9 +86,10 @@ def clearSelected(self):
|
|
77
86
|
|
78
87
|
@Sheet.api
|
79
88
|
@asyncthread
|
80
|
-
def select(self, rows, status=True, progress=True):
|
81
|
-
"Add *rows* to set of selected rows. Async. Don't show progress if *progress* is False; don't show status if *status* is False."
|
82
|
-
|
89
|
+
def select(self, rows, status=True, progress=True, add_undo=True):
|
90
|
+
"Add *rows* to set of selected rows. Async. Don't show progress if *progress* is False; don't show status if *status* is False. If *add_undo* is False, do not add an undo selection function to the undo history; useful for lowering memory consumption when caller is changing a large batch of selects in one command."
|
91
|
+
if add_undo:
|
92
|
+
self.addUndoSelection()
|
83
93
|
before = self.nSelectedRows
|
84
94
|
if self.options.bulk_select_clear:
|
85
95
|
self.clearSelected()
|
@@ -94,9 +104,10 @@ def select(self, rows, status=True, progress=True):
|
|
94
104
|
|
95
105
|
@Sheet.api
|
96
106
|
@asyncthread
|
97
|
-
def unselect(self, rows, status=True, progress=True):
|
98
|
-
"Remove *rows* from set of selected rows. Async. Don't show progress if *progress* is False; don't show status if *status* is False."
|
99
|
-
|
107
|
+
def unselect(self, rows, status=True, progress=True, add_undo=True):
|
108
|
+
"Remove *rows* from set of selected rows. Async. Don't show progress if *progress* is False; don't show status if *status* is False. If *add_undo* is False, do not add an undo unselection function to the undo history; useful for lowering memory consumption when caller is changing a large batch of selects in one command."
|
109
|
+
if add_undo:
|
110
|
+
self.addUndoSelection()
|
100
111
|
before = self.nSelectedRows
|
101
112
|
for r in (Progress(rows, 'unselecting') if progress else rows):
|
102
113
|
self.unselectRow(r)
|
@@ -191,10 +202,10 @@ Sheet.addCommand('\\', 'unselect-col-regex', 'unselectByIdx(searchInputRegex("un
|
|
191
202
|
Sheet.addCommand('g|', 'select-cols-regex', 'selectByIdx(searchInputRegex("select", columns="visibleCols"))', 'select rows matching regex in any visible column')
|
192
203
|
Sheet.addCommand('g\\', 'unselect-cols-regex', 'unselectByIdx(searchInputRegex("unselect", columns="visibleCols"))', 'unselect rows matching regex in any visible column')
|
193
204
|
|
194
|
-
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')
|
195
|
-
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')
|
196
|
-
Sheet.addCommand('z,', 'select-exact-cell', 'select(gatherBy(lambda r,c=cursorCol,v=cursorTypedValue: c.getTypedValue(r) == v), progress=False)', 'select rows matching current cell in current column')
|
197
|
-
Sheet.addCommand('gz,', 'select-exact-row', 'select(gatherBy(lambda r,currow=cursorRow,vcols=visibleCols: all([c.getTypedValue(r) == c.getTypedValue(currow) for c in vcols])), progress=False)', 'select rows matching current row in all visible columns')
|
205
|
+
Sheet.addCommand(',', 'select-equal-cell', 'select(gatherBy(lambda r,c=cursorCol,v=cursorDisplay: c.getDisplayValue(r) == v), progress=False)', 'select rows matching current cell displayed value in current column')
|
206
|
+
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 displayed values in current row in all visible columns')
|
207
|
+
Sheet.addCommand('z,', 'select-exact-cell', 'select(gatherBy(lambda r,c=cursorCol,v=cursorTypedValue: c.getTypedValue(r) == v), progress=False)', 'select rows matching current cell typed value in current column')
|
208
|
+
Sheet.addCommand('gz,', 'select-exact-row', 'select(gatherBy(lambda r,currow=cursorRow,vcols=visibleCols: all([c.getTypedValue(r) == c.getTypedValue(currow) for c in vcols])), progress=False)', 'select rows matching typed values in current row in all visible columns')
|
198
209
|
|
199
210
|
Sheet.addCommand('z|', 'select-expr', 'expr=inputExpr("select by expr: "); select(gatherBy(lambda r, sheet=sheet, expr=expr, curcol=cursorCol: sheet.evalExpr(expr, r, curcol=curcol)), progress=False)', 'select rows matching Python expression in any visible column')
|
200
211
|
Sheet.addCommand('z\\', 'unselect-expr', 'expr=inputExpr("unselect by expr: "); unselect(gatherBy(lambda r, sheet=sheet, expr=expr, curcol=cursorCol: sheet.evalExpr(expr, r, curcol=curcol)), progress=False)', 'unselect rows matching Python expression in any visible column')
|
visidata/settings.py
CHANGED
@@ -446,13 +446,17 @@ def loadConfigAndPlugins(vd, args=AttrDict()):
|
|
446
446
|
# autoload installed plugins first
|
447
447
|
args_plugins_autoload = args.plugins_autoload if 'plugins_autoload' in args else True
|
448
448
|
if not args.nothing and args_plugins_autoload and vd.options.plugins_autoload:
|
449
|
-
from
|
449
|
+
from importlib.metadata import entry_points
|
450
|
+
eps_visidata = []
|
450
451
|
try:
|
451
452
|
eps = entry_points()
|
452
|
-
|
453
|
+
vp = 'visidata.plugins'
|
454
|
+
if hasattr(eps, 'groups'): #Python >= 3.10
|
455
|
+
eps_visidata = eps.select(group=vp)
|
456
|
+
else: #Python < 3.10
|
457
|
+
eps_visidata = eps.get(vp, [])
|
453
458
|
except Exception as e:
|
454
|
-
|
455
|
-
vd.warning('plugin autoload failed; see issue #1529')
|
459
|
+
vd.warning(f'plugin autoload failed; see issue #1529: {e}')
|
456
460
|
|
457
461
|
for ep in eps_visidata:
|
458
462
|
try:
|
@@ -550,7 +554,7 @@ def setPersistentOptions(vd, **kwargs):
|
|
550
554
|
fp.write(f'options.{optname}={repr(optval)}\n')
|
551
555
|
|
552
556
|
|
553
|
-
vd.option('visidata_dir', '
|
557
|
+
vd.option('visidata_dir', user_config_dir('visidata'), 'directory to load and store additional files', sheettype=None)
|
554
558
|
|
555
559
|
BaseSheet.bindkey('^M', '^J') # for windows ENTER
|
556
560
|
|