visidata 3.1__py3-none-any.whl → 3.2__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 +70 -36
- visidata/_open.py +9 -6
- visidata/_types.py +2 -2
- visidata/aggregators.py +125 -16
- visidata/apps/vdsql/_ibis.py +8 -13
- visidata/basesheet.py +4 -1
- visidata/canvas.py +11 -7
- visidata/clipboard.py +11 -2
- visidata/cliptext.py +65 -23
- visidata/cmdlog.py +5 -1
- visidata/column.py +6 -2
- visidata/ddwplay.py +2 -2
- visidata/deprecated.py +91 -63
- visidata/errors.py +41 -5
- visidata/{features → experimental}/helloworld.py +1 -1
- visidata/expr.py +1 -0
- visidata/extensible.py +4 -0
- visidata/features/cmdpalette.py +3 -3
- visidata/features/describe.py +2 -2
- visidata/features/expand_cols.py +8 -5
- visidata/features/freeze.py +14 -2
- visidata/features/go_col.py +2 -1
- 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 -3
- visidata/features/melt.py +1 -0
- visidata/features/rank.py +103 -0
- visidata/features/reload_every.py +9 -6
- visidata/features/sysedit.py +14 -4
- visidata/features/transpose.py +1 -0
- visidata/features/window.py +12 -0
- visidata/form.py +4 -4
- visidata/freqtbl.py +47 -3
- visidata/fuzzymatch.py +8 -5
- visidata/graph.py +5 -3
- visidata/guides/AggregatorsSheet.md +84 -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 +3 -4
- visidata/indexsheet.py +1 -1
- visidata/loaders/_pandas.py +3 -1
- visidata/loaders/archive.py +6 -3
- visidata/loaders/csv.py +5 -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 +4 -3
- visidata/main.py +11 -5
- visidata/mainloop.py +7 -4
- visidata/man/parse_options.py +3 -2
- visidata/man/vd.1 +26 -14
- visidata/man/vd.txt +25 -14
- visidata/menu.py +9 -9
- visidata/metasheets.py +3 -3
- visidata/mouse.py +1 -0
- visidata/pyobj.py +17 -9
- visidata/save.py +5 -1
- visidata/selection.py +29 -18
- visidata/settings.py +2 -2
- visidata/sheets.py +52 -24
- visidata/shell.py +2 -2
- visidata/sidebar.py +4 -2
- visidata/sort.py +89 -11
- visidata/statusbar.py +10 -9
- visidata/tests/test_cliptext.py +151 -0
- visidata/tests/test_commands.py +5 -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 +16 -8
- visidata/undo.py +1 -1
- {visidata-3.1.data → visidata-3.2.data}/data/share/man/man1/vd.1 +26 -14
- {visidata-3.1.data → visidata-3.2.data}/data/share/man/man1/visidata.1 +26 -14
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/METADATA +62 -15
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/RECORD +95 -93
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/WHEEL +1 -1
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/entry_points.txt +1 -0
- visidata/features/errors_guide.py +0 -26
- visidata/loaders/api_bitio.py +0 -102
- visidata/stored_prop.py +0 -38
- visidata-3.1.data/scripts/vd +0 -6
- /visidata/{guides/SortGuide.md → vendor/__init__.py} +0 -0
- {visidata-3.1.data → visidata-3.2.data}/data/share/applications/visidata.desktop +0 -0
- {visidata-3.1.data → visidata-3.2.data}/scripts/vd2to3.vdx +0 -0
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-3.1.dist-info → visidata-3.2.dist-info}/top_level.txt +0 -0
visidata/clipboard.py
CHANGED
@@ -7,13 +7,21 @@ import tempfile
|
|
7
7
|
import functools
|
8
8
|
import os
|
9
9
|
import itertools
|
10
|
+
import platform
|
10
11
|
|
11
12
|
from visidata import VisiData, vd, asyncthread, SettableColumn
|
12
13
|
from visidata import Sheet, Path, Column
|
13
14
|
|
14
|
-
if
|
15
|
+
if (
|
16
|
+
# Windows
|
17
|
+
sys.platform == 'win32'
|
18
|
+
# WSL 2
|
19
|
+
or "microsoft-standard-WSL2" in platform.uname().release
|
20
|
+
# WSL 1
|
21
|
+
or sys.platform == 'linux' and platform.uname().release.endswith("-Microsoft")
|
22
|
+
):
|
15
23
|
syscopy_cmd_default = 'clip.exe'
|
16
|
-
syspaste_cmd_default = 'powershell -command Get-Clipboard'
|
24
|
+
syspaste_cmd_default = 'powershell.exe -noprofile -command Get-Clipboard'
|
17
25
|
elif sys.platform == 'darwin':
|
18
26
|
syscopy_cmd_default = 'pbcopy w'
|
19
27
|
syspaste_cmd_default = 'pbpaste'
|
@@ -196,6 +204,7 @@ Sheet.addCommand('Y', 'syscopy-row', 'syscopyCells(visibleCols, [cursorRow])', '
|
|
196
204
|
|
197
205
|
Sheet.addCommand('gY', 'syscopy-selected', 'syscopyCells(visibleCols, onlySelectedRows)', 'yank (copy) selected rows to system clipboard (using options.clipboard_copy_cmd)')
|
198
206
|
Sheet.addCommand('zY', 'syscopy-cell', 'syscopyValue(cursorDisplay)', 'yank (copy) current cell to system clipboard (using options.clipboard_copy_cmd)')
|
207
|
+
Sheet.addCommand('', 'syscopy-colname', 'syscopyValue(cursorCol.name)', 'yank (copy) current column header to system clipboard (using options.clipboard_copy_cmd)')
|
199
208
|
Sheet.addCommand('gzY', 'syscopy-cells', 'syscopyCells([cursorCol], onlySelectedRows, filetype="txt")', 'yank (copy) contents of current column from selected rows to system clipboard (using options.clipboard_copy_cmd')
|
200
209
|
|
201
210
|
Sheet.addCommand('x', 'cut-row', 'copyRows([sheet.delete_row(cursorRowIndex)]); defer and cursorDown(1)', 'delete (cut) current row and move it to clipboard')
|
visidata/cliptext.py
CHANGED
@@ -142,42 +142,52 @@ def iterchars(x):
|
|
142
142
|
|
143
143
|
@functools.lru_cache(maxsize=100000)
|
144
144
|
def _clipstr(s, dispw, trunch='', oddspacech='', combch='', modch=''):
|
145
|
-
'''
|
146
|
-
|
147
|
-
|
145
|
+
''' *s* is a string or an iterator that contains characters.
|
146
|
+
*dispw* is the integer screen width that the clipped string will fit inside, or None.
|
147
|
+
Return clipped string and width in terminal display characters.
|
148
|
+
Note: width may differ from len(s) if chars are 'fullwidth'.
|
149
|
+
If *dispw* is None, no clipping occurs.
|
150
|
+
If *trunch* has a width greater than *dispw*, the empty string
|
151
|
+
will be used as a truncator instead.'''
|
152
|
+
if not s or (dispw is not None and dispw < 1): #iterator s would be truthy
|
148
153
|
return '', 0
|
149
154
|
|
150
|
-
if dispw == 1:
|
151
|
-
return s[0], 1
|
152
|
-
|
153
155
|
w = 0
|
154
156
|
ret = ''
|
157
|
+
trunc_i = 0
|
158
|
+
w_truncated = 0
|
155
159
|
|
156
160
|
trunchlen = dispwidth(trunch)
|
161
|
+
if dispw is None:
|
162
|
+
s = ''.join(s)
|
163
|
+
return s, dispwidth(s)
|
164
|
+
if trunchlen > dispw: #if the truncator cannot fit, use a truncator of ''
|
165
|
+
return _clipstr(s, dispw, trunch='', oddspacech=oddspacech, combch=combch, modch=modch)
|
157
166
|
for c in s:
|
158
167
|
newc, chlen = _dispch(c, oddspacech=oddspacech, combch=combch, modch=modch)
|
159
168
|
if not newc:
|
160
169
|
newc = c
|
161
170
|
chlen = dispwidth(c)
|
162
171
|
|
163
|
-
if
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
ret += newc
|
172
|
+
#if the next character will fit
|
173
|
+
if w+chlen <= dispw:
|
174
|
+
ret += c
|
175
|
+
w += chlen
|
176
|
+
#move the truncation spot forward only when the truncation character can fit
|
177
|
+
if w+trunchlen <= dispw:
|
178
|
+
trunc_i += 1
|
179
|
+
w_truncated += chlen
|
180
|
+
continue
|
181
|
+
# if we reach this line, a character did not fit, and the result needs truncation
|
182
|
+
return ret[:trunc_i] + trunch, w_truncated+trunchlen
|
175
183
|
|
176
184
|
return ret, w
|
177
185
|
|
178
186
|
|
179
187
|
@drawcache
|
180
188
|
def clipstr(s, dispw, truncator=None, oddspace=None):
|
189
|
+
''' *s* is a string or an iterator that contains characters.
|
190
|
+
*dispw* is the integer screen width that the clipped string will fit inside, or None.'''
|
181
191
|
if options.visibility:
|
182
192
|
return _clipstr(s, dispw,
|
183
193
|
trunch=options.disp_truncator if truncator is None else truncator,
|
@@ -205,8 +215,6 @@ def clipdraw(scr, y, x, s, attr, w=None, clear=True, literal=False, **kwargs):
|
|
205
215
|
|
206
216
|
x = max(0, x)
|
207
217
|
y = max(0, y)
|
208
|
-
assert x >= 0, x
|
209
|
-
assert y >= 0, y
|
210
218
|
|
211
219
|
return clipdraw_chunks(scr, y, x, chunks, attr, w=w, clear=clear, **kwargs)
|
212
220
|
|
@@ -310,6 +318,7 @@ def wraptext(text, width=80, indent=''):
|
|
310
318
|
line = _markdown_to_internal(line)
|
311
319
|
chunks = re.split(internal_markup_re, line)
|
312
320
|
textchunks = [x for x in chunks if not is_vdcode(x)]
|
321
|
+
# textwrap.wrap does not handle variable-width characters #2416
|
313
322
|
for linenum, textline in enumerate(textwrap.wrap(''.join(textchunks), width=width, drop_whitespace=False)):
|
314
323
|
txt = textline
|
315
324
|
r = ''
|
@@ -345,8 +354,39 @@ def clipbox(scr, lines, attr, title=''):
|
|
345
354
|
for i, line in enumerate(lines):
|
346
355
|
clipdraw(scr, i+1, 2, line, attr)
|
347
356
|
|
348
|
-
clipdraw(scr, 0, w-
|
349
|
-
|
357
|
+
clipdraw(scr, 0, w-dispwidth(title)-6, f"| {title} |", attr)
|
358
|
+
|
359
|
+
def clipstr_start(dispval, w, truncator=''):
|
360
|
+
'''Return a tuple (frag, dw), where *frag* is the longest ending substring
|
361
|
+
of *dispval* that will fit in a space *w* terminal display characters wide,
|
362
|
+
and *dw* is the substring's display width as an int.'''
|
363
|
+
# Note: this implementation is likely incorrect for unusual Unicode
|
364
|
+
# strings or encodings, where trimming an initial character produces
|
365
|
+
# an invalid string or does not make the string shorter.
|
366
|
+
if w <= 0: return '', 0
|
367
|
+
j = len(dispval)
|
368
|
+
while j >= 1:
|
369
|
+
if dispwidth((truncator if j > 1 else '') + dispval[j-1:]) <= w:
|
370
|
+
j -= 1
|
371
|
+
else:
|
372
|
+
break
|
373
|
+
frag = (truncator if j > 0 else '') + dispval[j:]
|
374
|
+
return frag, dispwidth(frag)
|
375
|
+
|
376
|
+
def clipstr_middle(s, n=10, truncator='…'):
|
377
|
+
'''Return a string having a display width <= *n*. Excess characters are
|
378
|
+
trimmed from the middle of the string, and replaced by a single
|
379
|
+
instance of *truncator*.'''
|
380
|
+
if n == 0: return '', 0
|
381
|
+
if dispwidth(s) > n:
|
382
|
+
#for even widths, give the leftover 1 space to the right fragment
|
383
|
+
l_space = n//2 if n%2 == 1 else max(n//2-1, 0)
|
384
|
+
l_frag, l_w = _clipstr(s, l_space)
|
385
|
+
#if left fragment did not fill its space, give the unused space to the right fragment
|
386
|
+
r_frag = clipstr_start(s, n//2+(l_space-l_w))[0]
|
387
|
+
res = l_frag + truncator + r_frag
|
388
|
+
return res, dispwidth(res)
|
389
|
+
return s, dispwidth(s)
|
350
390
|
|
351
391
|
vd.addGlobals(clipstr=clipstr,
|
352
392
|
clipdraw=clipdraw,
|
@@ -355,4 +395,6 @@ vd.addGlobals(clipstr=clipstr,
|
|
355
395
|
dispwidth=dispwidth,
|
356
396
|
iterchars=iterchars,
|
357
397
|
iterchunks=iterchunks,
|
358
|
-
wraptext=wraptext
|
398
|
+
wraptext=wraptext,
|
399
|
+
clipstr_start=clipstr_start,
|
400
|
+
clipstr_middle=clipstr_middle)
|
visidata/cmdlog.py
CHANGED
@@ -330,6 +330,9 @@ def replay_sync(vd, cmdlog):
|
|
330
330
|
with vd.DisableAsync():
|
331
331
|
vd.sync() #2352 let cmdlog finish loading
|
332
332
|
cmdlog.cursorRowIndex = 0
|
333
|
+
# save current replay, for cmdlogs that replay other cmdlogs, such as a macro executing another macro
|
334
|
+
prev_replay = vd.currentReplay
|
335
|
+
prev_replay_row = vd.currentReplayRow
|
333
336
|
vd.currentReplay = cmdlog
|
334
337
|
|
335
338
|
with Progress(total=len(cmdlog.rows)) as prog:
|
@@ -356,7 +359,8 @@ def replay_sync(vd, cmdlog):
|
|
356
359
|
vd.activeSheet.ensureLoaded()
|
357
360
|
|
358
361
|
vd.status('replay complete')
|
359
|
-
vd.currentReplay =
|
362
|
+
vd.currentReplay = prev_replay
|
363
|
+
vd.currentReplayRow = prev_replay_row
|
360
364
|
|
361
365
|
|
362
366
|
@VisiData.api
|
visidata/column.py
CHANGED
@@ -249,6 +249,8 @@ class Column(Extensible):
|
|
249
249
|
|
250
250
|
if self.type is anytype:
|
251
251
|
if isinstance(typedval, (dict, list, tuple)):
|
252
|
+
if width is None:
|
253
|
+
return ''.join(iterchars(typedval))
|
252
254
|
dispval, dispw = clipstr(iterchars(typedval), width)
|
253
255
|
return dispval
|
254
256
|
|
@@ -264,7 +266,9 @@ class Column(Extensible):
|
|
264
266
|
|
265
267
|
The 'generic' displayer does not do any formatting.
|
266
268
|
'''
|
267
|
-
if width is not None and width > 1 and
|
269
|
+
if width is not None and width > 1 and \
|
270
|
+
vd.isNumeric(self) and \
|
271
|
+
isinstance(dw.typedval, (int, float)):
|
268
272
|
yield ('', dw.text.rjust(width-2))
|
269
273
|
else:
|
270
274
|
yield ('', dw.text)
|
@@ -470,7 +474,7 @@ class Column(Extensible):
|
|
470
474
|
break #1747 early out to speed up wide columns
|
471
475
|
w = w_max
|
472
476
|
w = max(w, nlen)+2
|
473
|
-
w = min(w, self.sheet.windowWidth)
|
477
|
+
w = min(w, self.sheet.windowWidth-1)
|
474
478
|
return w
|
475
479
|
|
476
480
|
|
visidata/ddwplay.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from collections import defaultdict
|
2
2
|
import json
|
3
3
|
import time
|
4
|
-
from visidata import colors, vd, clipdraw, ColorAttr
|
4
|
+
from visidata import colors, vd, clipdraw, ColorAttr, dispwidth
|
5
5
|
|
6
6
|
__all__ = ['Animation', 'AnimationMgr']
|
7
7
|
|
@@ -77,7 +77,7 @@ class Animation:
|
|
77
77
|
self.total_ms = sum(f.duration_ms or 0 for f in self.frames.values())
|
78
78
|
for f in self.frames.values():
|
79
79
|
for r, x, y, _ in self.iterdeep(f.rows):
|
80
|
-
self.width = max(self.width, x+
|
80
|
+
self.width = max(self.width, x+dispwidth(r.text))
|
81
81
|
self.height = max(self.height, y)
|
82
82
|
|
83
83
|
def draw(self, scr, *, t=0, x=0, y=0, loop=False, attr=ColorAttr(), **kwargs):
|
visidata/deprecated.py
CHANGED
@@ -3,12 +3,16 @@ import functools
|
|
3
3
|
from visidata import VisiData, vd
|
4
4
|
import visidata
|
5
5
|
|
6
|
-
|
6
|
+
def deprecated_alias(depver, *args, **kwargs):
|
7
|
+
# expand this to create cmd
|
8
|
+
# with cmd.deprecated=depver
|
9
|
+
return visidata.BaseSheet.bindkey(*args, **kwargs)
|
7
10
|
|
8
|
-
|
11
|
+
@VisiData.api
|
12
|
+
def deprecated_warn(vd, funcname, ver, instead):
|
9
13
|
import traceback
|
10
14
|
|
11
|
-
msg = f'{
|
15
|
+
msg = f'{funcname} deprecated since v{ver}'
|
12
16
|
if instead:
|
13
17
|
msg += f'; use {instead}'
|
14
18
|
|
@@ -20,18 +24,27 @@ def deprecated_warn(func, ver, instead):
|
|
20
24
|
vd.warning(f'Deprecated call traceback (most recent last):')
|
21
25
|
|
22
26
|
|
23
|
-
def deprecated(ver, instead=''):
|
27
|
+
def deprecated(ver, instead='', check=True):
|
24
28
|
def decorator(func):
|
25
29
|
@functools.wraps(func)
|
26
30
|
def wrapper(*args, **kwargs):
|
27
|
-
deprecated_warn(
|
31
|
+
vd.deprecated_warn(wrapper.__name__, ver, instead)
|
28
32
|
return func(*args, **kwargs)
|
33
|
+
|
34
|
+
if check and hasattr(func, '_extensible_api'):
|
35
|
+
vd.error(f"{func.__name__}: @deprecated applied in wrong order") #2623
|
36
|
+
|
29
37
|
return wrapper
|
30
38
|
return decorator
|
31
39
|
|
32
40
|
|
33
|
-
|
41
|
+
def _deprecated_api(ver, instead=''):
|
42
|
+
'Decorator to deliberately wrap non-deprecated .api functions as a deprecated global function. Use @deprecated instead, except in deprecated.py.'
|
43
|
+
return deprecated(ver, instead, check=False)
|
44
|
+
|
45
|
+
|
34
46
|
@VisiData.api
|
47
|
+
@deprecated('1.6', 'vd instead of vd()')
|
35
48
|
def __call__(vd):
|
36
49
|
'Deprecated; use plain "vd"'
|
37
50
|
return vd
|
@@ -39,7 +52,7 @@ def __call__(vd):
|
|
39
52
|
|
40
53
|
@deprecated('1.6')
|
41
54
|
def copyToClipboard(value):
|
42
|
-
vd.error("copyToClipboard longer implemented")
|
55
|
+
vd.error("copyToClipboard no longer implemented")
|
43
56
|
return visidata.clipboard_copy(value)
|
44
57
|
|
45
58
|
|
@@ -62,32 +75,32 @@ def bindkey_override(keystrokes, longname):
|
|
62
75
|
bindkey = visidata.BaseSheet.bindkey
|
63
76
|
unbindkey = visidata.BaseSheet.unbindkey
|
64
77
|
|
65
|
-
@deprecated('2.0')
|
66
78
|
@visidata.Sheet.api
|
79
|
+
@deprecated('2.0')
|
67
80
|
def exec_keystrokes(self, keystrokes, vdglobals=None):
|
68
81
|
return self.execCommand(self.getCommand(keystrokes), vdglobals, keystrokes=keystrokes)
|
69
82
|
|
70
83
|
visidata.Sheet.exec_command = deprecated('2.0')(visidata.Sheet.execCommand)
|
71
84
|
|
72
|
-
@deprecated('2.0', 'def open_<filetype> instead')
|
73
85
|
@VisiData.api
|
86
|
+
@deprecated('2.0', 'def open_<filetype> instead')
|
74
87
|
def filetype(vd, ext, constructor):
|
75
88
|
'Add constructor to handle the given file type/extension.'
|
76
89
|
globals().setdefault('open_'+ext, lambda p,ext=ext: constructor(p.base_stem, source=p, filetype=ext))
|
77
90
|
|
78
|
-
@deprecated('2.0', 'Sheet(namepart1, namepart2, ...)')
|
79
91
|
@VisiData.global_api
|
92
|
+
@deprecated('2.0', 'Sheet(namepart1, namepart2, ...)')
|
80
93
|
def joinSheetnames(vd, *sheetnames):
|
81
94
|
'Concatenate sheet names in a standard way'
|
82
95
|
return visidata.options.name_joiner.join(str(x) for x in sheetnames)
|
83
96
|
|
84
|
-
@deprecated('2.0', 'PyobjSheet')
|
85
97
|
@VisiData.global_api
|
98
|
+
@deprecated('2.0', 'PyobjSheet')
|
86
99
|
def load_pyobj(*names, **kwargs):
|
87
100
|
return visidata.PyobjSheet(*names, **kwargs)
|
88
101
|
|
89
|
-
@deprecated('2.0', 'PyobjSheet')
|
90
102
|
@VisiData.global_api
|
103
|
+
@deprecated('2.0', 'PyobjSheet')
|
91
104
|
def push_pyobj(name, pyobj):
|
92
105
|
vs = visidata.PyobjSheet(name, source=pyobj)
|
93
106
|
if vs:
|
@@ -103,33 +116,33 @@ visidata.addGlobals({'load_pyobj': load_pyobj, 'isNumeric': isNumeric})
|
|
103
116
|
|
104
117
|
# The longnames on the left are deprecated for 2.0
|
105
118
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
119
|
+
deprecated_alias('2.0', 'edit-cells', 'setcol-input')
|
120
|
+
deprecated_alias('2.0', 'fill-nulls', 'setcol-fill')
|
121
|
+
deprecated_alias('2.0', 'paste-cells', 'setcol-clipboard')
|
122
|
+
deprecated_alias('2.0', 'frequency-rows', 'frequency-summary')
|
123
|
+
deprecated_alias('2.0', 'dup-cell', 'dive-cell')
|
124
|
+
deprecated_alias('2.0', 'dup-row', 'dive-row')
|
125
|
+
deprecated_alias('2.0', 'next-search', 'search-next')
|
126
|
+
deprecated_alias('2.0', 'prev-search', 'search-prev')
|
127
|
+
deprecated_alias('2.0', 'search-prev', 'searchr-next')
|
128
|
+
deprecated_alias('2.0', 'prev-sheet', 'jump-prev')
|
129
|
+
deprecated_alias('2.0', 'prev-value', 'go-prev-value')
|
130
|
+
deprecated_alias('2.0', 'next-value', 'go-next-value')
|
131
|
+
deprecated_alias('2.0', 'prev-selected', 'go-prev-selected')
|
132
|
+
deprecated_alias('2.0', 'next-selected', 'go-next-selected')
|
133
|
+
deprecated_alias('2.0', 'prev-null', 'go-prev-null')
|
134
|
+
deprecated_alias('2.0', 'next-null', 'go-next-null')
|
135
|
+
deprecated_alias('2.0', 'page-right', 'go-right-page')
|
136
|
+
deprecated_alias('2.0', 'page-left', 'go-left-page')
|
137
|
+
deprecated_alias('2.0', 'dive-cell', 'open-cell')
|
138
|
+
deprecated_alias('2.0', 'dive-row', 'open-row')
|
139
|
+
deprecated_alias('2.0', 'add-sheet', 'open-new')
|
140
|
+
deprecated_alias('2.0', 'save-sheets-selected', 'save-selected')
|
141
|
+
deprecated_alias('2.0', 'join-sheets', 'join-selected')
|
142
|
+
deprecated_alias('2.0', 'dive-rows', 'dive-selected')
|
130
143
|
|
131
144
|
# v2.3
|
132
|
-
|
145
|
+
deprecated_alias('2.3', 'show-aggregate', 'memo-aggregate')
|
133
146
|
#theme('use_default_colors', True, 'curses use default terminal colors')
|
134
147
|
#option('expand_col_scanrows', 1000, 'number of rows to check when expanding columns (0 = all)')
|
135
148
|
|
@@ -149,25 +162,25 @@ def load_tsv(fn):
|
|
149
162
|
|
150
163
|
# NOTE: you cannot use deprecated() with nonfuncs
|
151
164
|
|
152
|
-
cancelThread =
|
153
|
-
status =
|
154
|
-
warning =
|
155
|
-
error =
|
156
|
-
debug =
|
157
|
-
fail =
|
165
|
+
cancelThread = _deprecated_api('2.6', 'vd.cancelThread')(vd.cancelThread)
|
166
|
+
status = _deprecated_api('2.6', 'vd.status')(vd.status)
|
167
|
+
warning = _deprecated_api('2.6', 'vd.warning')(vd.warning)
|
168
|
+
error = _deprecated_api('2.6', 'vd.error')(vd.error)
|
169
|
+
debug = _deprecated_api('2.6', 'vd.debug')(vd.debug)
|
170
|
+
fail = _deprecated_api('2.6', 'vd.fail')(vd.fail)
|
158
171
|
|
159
172
|
option = theme = vd.option # deprecated('2.6', 'vd.option')(vd.option)
|
160
173
|
jointypes = vd.jointypes # deprecated('2.6', 'vd.jointypes')(vd.jointypes)
|
161
|
-
confirm =
|
162
|
-
launchExternalEditor =
|
163
|
-
launchEditor =
|
164
|
-
exceptionCaught =
|
165
|
-
openSource =
|
174
|
+
confirm = _deprecated_api('2.6', 'vd.confirm')(vd.confirm)
|
175
|
+
launchExternalEditor = _deprecated_api('2.6', 'vd.launchExternalEditor')(vd.launchExternalEditor)
|
176
|
+
launchEditor = _deprecated_api('2.6', 'vd.launchEditor')(vd.launchEditor)
|
177
|
+
exceptionCaught = _deprecated_api('2.6', 'vd.exceptionCaught')(vd.exceptionCaught)
|
178
|
+
openSource = _deprecated_api('2.6', 'vd.openSource')(vd.openSource)
|
166
179
|
globalCommand = visidata.BaseSheet.addCommand
|
167
|
-
visidata.Sheet.StaticColumn =
|
180
|
+
visidata.Sheet.StaticColumn = _deprecated_api('2.11', 'Sheet.freeze_col')(visidata.Sheet.freeze_col)
|
168
181
|
#visidata.Path.open_text = deprecated('3.0', 'visidata.Path.open')(visidata.Path.open) # undeprecated in 3.1
|
169
182
|
|
170
|
-
vd.sysclip_value =
|
183
|
+
vd.sysclip_value = _deprecated_api('3.0', 'vd.sysclipValue')(vd.sysclipValue)
|
171
184
|
|
172
185
|
def itemsetter(i):
|
173
186
|
def g(obj, v):
|
@@ -181,8 +194,8 @@ vd.optalias('confirm_overwrite', 'overwrite', 'confirm')
|
|
181
194
|
vd.optalias('show_graph_labels', 'disp_graph_labels')
|
182
195
|
vd.optalias('zoom_incr', 'disp_zoom_incr')
|
183
196
|
|
184
|
-
|
185
|
-
|
197
|
+
deprecated_alias('3.0', 'visibility-sheet', 'toggle-multiline')
|
198
|
+
deprecated_alias('3.0', 'visibility-col', 'toggle-multiline')
|
186
199
|
|
187
200
|
def clean_to_id(s):
|
188
201
|
return visidata.vd.cleanName(s)
|
@@ -204,7 +217,7 @@ class OnExit:
|
|
204
217
|
except Exception as e:
|
205
218
|
vd.exceptionCaught(e)
|
206
219
|
|
207
|
-
|
220
|
+
deprecated_alias('3.0', 'open-inputs', 'open-input-history')
|
208
221
|
|
209
222
|
#vd.option('plugins_url', 'https://visidata.org/plugins/plugins.jsonl', 'source of plugins sheet')
|
210
223
|
|
@@ -216,37 +229,52 @@ def inputRegexSubstOld(vd, prompt):
|
|
216
229
|
return dict(before=before, after=after)
|
217
230
|
|
218
231
|
|
219
|
-
visidata.Sheet.addCommand('', 'addcol-subst', 'addColumnAtCursor(Column(cursorCol.name + "_re", getter=regexTransform(cursorCol, **inputRegexSubstOld("transform column by regex: "))))', 'add column derived from current column, replacing regex with subst (may include \1 backrefs)', deprecated=
|
220
|
-
visidata.Sheet.addCommand('', 'setcol-subst', 'setValuesFromRegex([cursorCol], someSelectedRows, **inputRegexSubstOld("transform column by regex: "))', 'regex/subst - modify selected rows in current column, replacing regex with subst, (may include backreferences \\1 etc)', deprecated=
|
221
|
-
visidata.Sheet.addCommand('', 'setcol-subst-all', 'setValuesFromRegex(visibleCols, someSelectedRows, **inputRegexSubstOld(f"transform {nVisibleCols} columns by regex: "))', 'modify selected rows in all visible columns, replacing regex with subst (may include \\1 backrefs)', deprecated=
|
232
|
+
visidata.Sheet.addCommand('', 'addcol-subst', 'addColumnAtCursor(Column(cursorCol.name + "_re", getter=regexTransform(cursorCol, **inputRegexSubstOld("transform column by regex: "))))', 'add column derived from current column, replacing regex with subst (may include \1 backrefs)', deprecated='3.0')
|
233
|
+
visidata.Sheet.addCommand('', 'setcol-subst', 'setValuesFromRegex([cursorCol], someSelectedRows, **inputRegexSubstOld("transform column by regex: "))', 'regex/subst - modify selected rows in current column, replacing regex with subst, (may include backreferences \\1 etc)', deprecated='3.0')
|
234
|
+
visidata.Sheet.addCommand('', 'setcol-subst-all', 'setValuesFromRegex(visibleCols, someSelectedRows, **inputRegexSubstOld(f"transform {nVisibleCols} columns by regex: "))', 'modify selected rows in all visible columns, replacing regex with subst (may include \\1 backrefs)', deprecated='3.0')
|
222
235
|
|
223
|
-
visidata.Sheet.addCommand('', 'split-col', 'addRegexColumns(makeRegexSplitter, cursorCol, inputRegex("split regex: ", type="regex-split"))', 'Add new columns from regex split', deprecated=
|
224
|
-
visidata.Sheet.addCommand('', 'capture-col', 'addRegexColumns(makeRegexMatcher, cursorCol, inputRegex("capture regex: ", type="regex-capture"))', 'add new column from capture groups of regex; requires example row', deprecated=
|
236
|
+
visidata.Sheet.addCommand('', 'split-col', 'addRegexColumns(makeRegexSplitter, cursorCol, inputRegex("split regex: ", type="regex-split"))', 'Add new columns from regex split', deprecated='3.0')
|
237
|
+
visidata.Sheet.addCommand('', 'capture-col', 'addRegexColumns(makeRegexMatcher, cursorCol, inputRegex("capture regex: ", type="regex-capture"))', 'add new column from capture groups of regex; requires example row', deprecated='3.0')
|
225
238
|
|
226
239
|
#vd.option('cmdlog_histfile', '', 'file to autorecord each cmdlog action to', sheettype=None)
|
227
240
|
#BaseSheet.bindkey('KEY_BACKSPACE', 'menu-help')
|
228
241
|
|
229
|
-
@deprecated('3.0', 'vd.callNoExceptions(col.setValue, row, value)')
|
230
242
|
@visidata.Column.api
|
243
|
+
@deprecated('3.0', 'vd.callNoExceptions(col.setValue, row, value)')
|
231
244
|
def setValueSafe(self, row, value):
|
232
245
|
'setValue and ignore exceptions.'
|
233
246
|
return vd.callNoExceptions(self.setValue, row, value)
|
234
247
|
|
235
|
-
@deprecated('3.0', 'vd.callNoExceptions(sheet.checkCursor)')
|
236
248
|
@visidata.BaseSheet.api
|
249
|
+
@deprecated('3.0', 'vd.callNoExceptions(sheet.checkCursor)')
|
237
250
|
def checkCursorNoExceptions(sheet):
|
238
251
|
return vd.callNoExceptions(sheet.checkCursor)
|
239
252
|
|
240
|
-
@deprecated('3.1', 'vd.memoValue(name, value, displayvalue)')
|
241
253
|
@VisiData.api
|
254
|
+
@deprecated('3.1', 'vd.memoValue(name, value, displayvalue)')
|
242
255
|
def memo(vd, name, col, row):
|
243
256
|
return vd.memoValue(name, col.getTypedValue(row), col.getDisplayValue(row))
|
244
257
|
|
245
|
-
|
258
|
+
deprecated_alias('3.1', 'view-cell', 'pyobj-cell')
|
246
259
|
|
247
260
|
vd.optalias('textwrap_cells', 'disp_wrap_max_lines', 3) # wordwrap text for multiline rows
|
248
261
|
|
249
|
-
@deprecated('3.1', 'sheet.rowname(row)')
|
250
262
|
@visidata.TableSheet.api
|
263
|
+
@deprecated('3.1', 'sheet.rowname(row)')
|
251
264
|
def keystr(sheet, row):
|
252
265
|
return sheet.rowname(row)
|
266
|
+
|
267
|
+
vd.optalias('color_refline', 'color_graph_refline') # color_refline was used in v3.1 by mistake
|
268
|
+
|
269
|
+
@visidata.TableSheet.api
|
270
|
+
@deprecated('3.2', '[self.unsetKeys([c]) if c.keycol else self.setKeys([c]) for c in cols]')
|
271
|
+
def toggleKeys(self, cols):
|
272
|
+
for col in cols:
|
273
|
+
if col.keycol:
|
274
|
+
self.unsetKeys([col])
|
275
|
+
else:
|
276
|
+
self.setKeys([col])
|
277
|
+
|
278
|
+
vd.optalias('disp_pixel_random', 'disp_graph_pixel_random') #2661
|
279
|
+
|
280
|
+
vd.addGlobals(deprecated_warn=deprecated_warn)
|
visidata/errors.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
import traceback
|
2
|
+
import sys
|
3
|
+
import re
|
2
4
|
|
3
5
|
from visidata import vd, VisiData
|
4
6
|
|
@@ -10,10 +12,43 @@ class ExpectedException(Exception):
|
|
10
12
|
pass
|
11
13
|
|
12
14
|
|
13
|
-
def stacktrace(e=None):
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def stacktrace(e=None, exclude_caller=False):
|
16
|
+
'''Return a list of strings for the stack trace, without newlines
|
17
|
+
at the end. If an exception handler is executing, and *e* is none,
|
18
|
+
the stack trace includes extra levels of callers beyond the level
|
19
|
+
where the exception was caught. If *exclude_caller* is True, the
|
20
|
+
trace will exclude the function that called stacktrace(). The
|
21
|
+
trace will exclude several uninformative levels that are run
|
22
|
+
in interactive visidata.'''
|
23
|
+
|
24
|
+
if e:
|
25
|
+
return traceback.format_exception_only(type(e), e)
|
26
|
+
#in Python 3.11 we can replace sys.exc_info() with sys.exception()
|
27
|
+
handling = (sys.exc_info() != (None, None, None))
|
28
|
+
|
29
|
+
stack = ''.join(traceback.format_stack()).strip().splitlines()
|
30
|
+
|
31
|
+
if handling:
|
32
|
+
trim_levels = 2 # remove levels for stacktrace() -> format_stack()
|
33
|
+
if exclude_caller:
|
34
|
+
trim_levels += 1
|
35
|
+
trace_above = stack[:-2*trim_levels]
|
36
|
+
else:
|
37
|
+
trace_above = stack
|
38
|
+
if trace_above:
|
39
|
+
trace_above[0] = ' ' + trace_above[0] #fix indent level of first line
|
40
|
+
try:
|
41
|
+
# remove several levels of uninformative stacktrace in typical interactive vd
|
42
|
+
idx = trace_above.index(' ret = vd.mainloop(scr)')
|
43
|
+
trace_above = trace_above[idx+1:]
|
44
|
+
except ValueError:
|
45
|
+
pass
|
46
|
+
if not handling:
|
47
|
+
return trace_above
|
48
|
+
# remove lines that mark error columns with carets and sometimes tildes
|
49
|
+
trace_below = [ line for line in traceback.format_exc().strip().splitlines() if not re.match('^ *~*\\^+$', line) ]
|
50
|
+
# move the "Traceback (most recent call last) header to the top of the output
|
51
|
+
return [trace_below[0]] + trace_above + trace_below[1:]
|
17
52
|
|
18
53
|
|
19
54
|
@VisiData.api
|
@@ -21,7 +56,8 @@ def exceptionCaught(vd, exc=None, status=True, **kwargs):
|
|
21
56
|
'Add *exc* to list of last errors and add to status history. Show on left status bar if *status* is True. Reraise exception if options.debug is True.'
|
22
57
|
if isinstance(exc, ExpectedException): # already reported, don't log
|
23
58
|
return
|
24
|
-
|
59
|
+
# save a stack trace that does not include this function
|
60
|
+
vd.lastErrors.append(stacktrace(exclude_caller=True))
|
25
61
|
if status:
|
26
62
|
vd.status(f'{type(exc).__name__}: {exc}', priority=2)
|
27
63
|
else:
|
@@ -7,4 +7,4 @@ from visidata import vd, BaseSheet
|
|
7
7
|
|
8
8
|
vd.option('hello_world', '¡Hola mundo!', 'shown by the hello-world command')
|
9
9
|
|
10
|
-
BaseSheet.addCommand('
|
10
|
+
BaseSheet.addCommand('F2', 'hello-world', 'status(options.hello_world)', 'print greeting to status')
|
visidata/expr.py
CHANGED
@@ -100,6 +100,7 @@ Sheet.addCommand('=', 'addcol-expr', 'addColumnAtCursor(ExprColumn(inputExpr("ne
|
|
100
100
|
Sheet.addCommand('g=', 'setcol-expr', 'cursorCol.setValuesFromExpr(someSelectedRows, inputExpr("set selected="), curcol=cursorCol)', 'set current column for selected rows to result of Python expression')
|
101
101
|
Sheet.addCommand('z=', 'setcell-expr', 'cursorCol.setValues([cursorRow], evalExpr(inputExpr("set expr="), row=cursorRow, curcol=cursorCol))', 'evaluate Python expression on current row and set current cell with result of Python expression')
|
102
102
|
Sheet.addCommand('gz=', 'setcol-iter', 'cursorCol.setValues(someSelectedRows, *list(itertools.islice(eval(input("set column= ", "expr", completer=CompleteExpr())), len(someSelectedRows))))', 'set current column for selected rows to the items in result of Python sequence expression')
|
103
|
+
Sheet.addCommand('', 'addcol-iter', 'iter_expr=inputExpr("new column iterator expr: "); it = eval(iter_expr, getGlobals()); c=SettableColumn(); addColumnAtCursor(c); c.setValues(rows, *it)', 'add column with values from a Python sequence expression, repeating it if needed to fill')
|
103
104
|
|
104
105
|
Sheet.addCommand(None, 'show-expr', 'status(evalExpr(inputExpr("show expr="), row=cursorRow, curcol=cursorCol))', 'evaluate Python expression on current row and show result on status line')
|
105
106
|
|
visidata/extensible.py
CHANGED
@@ -50,6 +50,7 @@ class Extensible:
|
|
50
50
|
from visidata import vd
|
51
51
|
func.importingModule = vd.importingModule
|
52
52
|
setattr(cls, func.__name__, func)
|
53
|
+
func._extensible_api = True
|
53
54
|
return func
|
54
55
|
|
55
56
|
@classmethod
|
@@ -97,6 +98,7 @@ class Extensible:
|
|
97
98
|
oldfunc = getattr(cls, name, None)
|
98
99
|
if oldfunc:
|
99
100
|
func = wraps(oldfunc)(func)
|
101
|
+
func._extensible_api = True
|
100
102
|
setattr(cls, name, func)
|
101
103
|
return func
|
102
104
|
|
@@ -107,6 +109,7 @@ class Extensible:
|
|
107
109
|
def dofunc(self):
|
108
110
|
return func(self)
|
109
111
|
setattr(cls, func.__name__, dofunc)
|
112
|
+
func._extensible_api = True
|
110
113
|
return dofunc
|
111
114
|
|
112
115
|
@classmethod
|
@@ -121,6 +124,7 @@ class Extensible:
|
|
121
124
|
setattr(self, name, func(self))
|
122
125
|
return getattr(self, name)
|
123
126
|
setattr(cls, func.__name__, get_if_not)
|
127
|
+
func._extensible_api = True
|
124
128
|
return get_if_not
|
125
129
|
|
126
130
|
@classmethod
|
visidata/features/cmdpalette.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import collections
|
2
2
|
from functools import partial
|
3
|
-
from visidata import DrawablePane, BaseSheet, vd, VisiData, CompleteKey, clipdraw, HelpSheet, colors, AcceptInput, AttrDict, drawcache_property
|
3
|
+
from visidata import DrawablePane, BaseSheet, vd, VisiData, CompleteKey, clipdraw, HelpSheet, colors, AcceptInput, AttrDict, drawcache_property, dispwidth
|
4
4
|
|
5
5
|
|
6
6
|
vd.theme_option('color_cmdpalette', 'black on 72', 'base color of command palette')
|
@@ -180,7 +180,7 @@ def inputLongname(sheet):
|
|
180
180
|
formatted_name = f'[:bold][:onclick {row.longname}]{formatted_longname}[/][/]'
|
181
181
|
if vd.options.debug and match:
|
182
182
|
keystrokes = f'[{match.score}]'
|
183
|
-
r = f' [:keystrokes]{keystrokes.rjust(
|
183
|
+
r = f' [:keystrokes]{keystrokes.rjust(dispwidth(prompt)-5)}[/] '
|
184
184
|
if trigger_key:
|
185
185
|
r += f'[:keystrokes]{trigger_key}[/]'
|
186
186
|
else:
|
@@ -213,4 +213,4 @@ def exec_longname(sheet, longname):
|
|
213
213
|
|
214
214
|
|
215
215
|
vd.addCommand('Space', 'exec-longname', 'exec_longname(inputLongname())', 'execute command by its longname')
|
216
|
-
vd.addCommand('zSpace', 'exec-longname-simple', 'exec_longname(inputLongnameSimple())', 'execute command by its longname')
|
216
|
+
vd.addCommand('zSpace', 'exec-longname-simple', 'exec_longname(inputLongnameSimple())', 'execute command by its longname (without command palette)')
|