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/tests/test_cliptext.py
CHANGED
@@ -2,6 +2,7 @@ import pytest
|
|
2
2
|
from unittest.mock import Mock, call
|
3
3
|
|
4
4
|
import visidata
|
5
|
+
from visidata import iterchars
|
5
6
|
|
6
7
|
|
7
8
|
class TestClipText:
|
@@ -19,12 +20,175 @@ class TestClipText:
|
|
19
20
|
(' jsonl', 5, ' jso…', 5),
|
20
21
|
('abcdで', 6, 'abcdで', 6),
|
21
22
|
('abcdで', 5, 'abcd…', 5),
|
23
|
+
('a', 1, 'a', 1),
|
24
|
+
('ab', 1, '…', 1),
|
25
|
+
('abc', 2, 'a…', 2),
|
26
|
+
('で', 1, '…', 1),
|
27
|
+
('でで', 1, '…', 1),
|
28
|
+
('でで', 2, '…', 1),
|
29
|
+
('でで', 3, 'で…', 3),
|
30
|
+
('ででで', 4, 'で…', 3),
|
31
|
+
('ででで', 5, 'でで…', 5),
|
32
|
+
('', 1, '', 0),
|
33
|
+
('', None, '', 0),
|
34
|
+
('abcdef', None, 'abcdef', 6),
|
35
|
+
('ででで', None, 'ででで', 6),
|
36
|
+
('で'*100, None, 'で'*100, 2*100),
|
37
|
+
(iterchars([1,2,3]), 4, '[3]…', 4),
|
38
|
+
(iterchars([1,2,3]), 7, '[3] 1;…', 7),
|
39
|
+
(iterchars([1,2,3]), 9, '[3] 1; 2…', 9),
|
40
|
+
(iterchars([1,2,3]), 11, '[3] 1; 2; 3', 11),
|
41
|
+
(iterchars([1,2,3]), 12, '[3] 1; 2; 3', 11),
|
42
|
+
(iterchars({'a':1, 'b':2, 'c':3}), 7, '{3} a=…', 7),
|
43
|
+
(iterchars({'a':1, 'b':2, 'c':3}), 15, '{3} a=1 b=2 c=3', 15),
|
44
|
+
(iterchars({'a':1, 'b':2, 'で':3}), 13, '{3} a=1 b=2 …', 13),
|
45
|
+
(iterchars({'a':1, 'b':2, 'で':3}), 16, '{3} a=1 b=2 で=3', 16),
|
22
46
|
])
|
23
47
|
def test_clipstr(self, s, w, clippeds, clippedw):
|
24
48
|
clips, clipw = visidata.clipstr(s, w)
|
25
49
|
assert clips == clippeds
|
26
50
|
assert clipw == clippedw
|
27
51
|
|
52
|
+
@pytest.mark.parametrize('s, w, clippeds, clippedw', [
|
53
|
+
('b to', 4, 'b to', 4),
|
54
|
+
('abcde', 8, 'abcde', 5),
|
55
|
+
(' jsonl', 5, ' jsあ', 5),
|
56
|
+
('abcdで', 6, 'abcdで', 6),
|
57
|
+
('abcdで', 5, 'abcあ', 5),
|
58
|
+
('a', 1, 'a', 1),
|
59
|
+
('ab', 1, 'a', 1),
|
60
|
+
('abc', 2, 'あ', 2),
|
61
|
+
('で', 1, '', 0),
|
62
|
+
('でで', 1, '', 0),
|
63
|
+
('でで', 2, 'あ', 2),
|
64
|
+
('でで', 3, 'あ', 2),
|
65
|
+
('ででで', 4, 'であ', 4),
|
66
|
+
('ででで', 5, 'であ', 4),
|
67
|
+
('', 1, '', 0),
|
68
|
+
('', None, '', 0),
|
69
|
+
('abcdef', None, 'abcdef', 6),
|
70
|
+
('ででで', None, 'ででで', 6),
|
71
|
+
('で'*100, None, 'で'*100, 2*100),
|
72
|
+
(iterchars([1,2,3]), 1, '[', 1),
|
73
|
+
(iterchars({'a':1, 'b':2, 'c':3}), 1, '{', 1),
|
74
|
+
])
|
75
|
+
def test_clipstr_wide_truncator(self, s, w, clippeds, clippedw):
|
76
|
+
clips, clipw = visidata.clipstr(s, w, truncator='あ')
|
77
|
+
assert clips == clippeds
|
78
|
+
assert clipw == clippedw
|
79
|
+
|
80
|
+
@pytest.mark.parametrize('s, w, clippeds, clippedw', [
|
81
|
+
('b to', 4, 'b to', 4),
|
82
|
+
('abcde', 8, 'abcde', 5),
|
83
|
+
(' jsonl', 5, ' json', 5),
|
84
|
+
('abcdで', 6, 'abcdで', 6),
|
85
|
+
('abcdで', 5, 'abcd', 4),
|
86
|
+
('a', 1, 'a', 1),
|
87
|
+
('ab', 1, 'a', 1),
|
88
|
+
('abc', 2, 'ab', 2),
|
89
|
+
('で', 1, '', 0),
|
90
|
+
('でで', 1, '', 0),
|
91
|
+
('でで', 2, 'で', 2),
|
92
|
+
('でで', 3, 'で', 2),
|
93
|
+
('ででで', 4, 'でで', 4),
|
94
|
+
('ででで', 5, 'でで', 4),
|
95
|
+
('', 1, '', 0),
|
96
|
+
('', None, '', 0),
|
97
|
+
('abcdef', None, 'abcdef', 6),
|
98
|
+
('ででで', None, 'ででで', 6),
|
99
|
+
('で'*100, None, 'で'*100, 2*100),
|
100
|
+
])
|
101
|
+
def test_clipstr_empty_truncator(self, s, w, clippeds, clippedw):
|
102
|
+
clips, clipw = visidata.clipstr(s, w, truncator='')
|
103
|
+
assert clips == clippeds
|
104
|
+
assert clipw == clippedw
|
105
|
+
|
106
|
+
@pytest.mark.parametrize('s, w, truncator, clippeds, clippedw', [
|
107
|
+
('first\nsecond\n\nthird\n\n\n', 22, '', 'first·second··third···', 22),
|
108
|
+
('first\nsecond\n\nthird\n\n\n', 22, '…', 'first·second··third···', 22),
|
109
|
+
('first\nsecond\n\nthird\n\n\n', 21, '', 'first·second··third··', 21),
|
110
|
+
('first\nsecond\n\nthird\n\n\n', 21, '…', 'first·second··third·…', 21),
|
111
|
+
(''.join([chr(i) for i in range(256)]), 256, '',
|
112
|
+
'································ !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~··································¡¢£¤¥¦§¨©ª«¬\xad®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', 256),
|
113
|
+
])
|
114
|
+
def test_clipstr_unprintable(self, s, w, truncator, clippeds, clippedw):
|
115
|
+
clips, clipw = visidata.clipstr(s, w, truncator=truncator, oddspace='·')
|
116
|
+
assert clips == clippeds
|
117
|
+
assert clipw == clippedw
|
118
|
+
|
119
|
+
@pytest.mark.parametrize('s, w, clippeds, clippedw', [
|
120
|
+
('b to', 4, 'b to', 4),
|
121
|
+
('abcde', 8, 'abcde', 5),
|
122
|
+
(' jsonl', 5, 'jsonl', 5),
|
123
|
+
('abcdで', 6, 'abcdで', 6),
|
124
|
+
('abcdで', 5, 'bcdで', 5),
|
125
|
+
('でbcdで', 6, 'bcdで', 5),
|
126
|
+
('でbcdefghiで', 10, 'bcdefghiで', 10),
|
127
|
+
('でbcdefghiで', 3, 'iで', 3),
|
128
|
+
('でbcdで', 2, 'で', 2),
|
129
|
+
('でbcdで', 1, '', 0),
|
130
|
+
('でbcdで', 0, '', 0),
|
131
|
+
('でbcdで', -1, '', 0),
|
132
|
+
])
|
133
|
+
def test_clipstr_start(self, s, w, clippeds, clippedw):
|
134
|
+
clips, clipw = visidata.clipstr_start(s, w)
|
135
|
+
assert clips == clippeds
|
136
|
+
assert clipw == clippedw
|
137
|
+
|
138
|
+
@pytest.mark.parametrize('s, w, clippeds, clippedw', [
|
139
|
+
('aAbcで', 6, 'aAbcで', 6),
|
140
|
+
('aAbcで', 7, 'aAbcで', 6),
|
141
|
+
('aAbcで', 1000, 'aAbcで', 6),
|
142
|
+
('aAbcで', 5, '…bcで', 5),
|
143
|
+
('でbcで', 5, '…bcで', 5),
|
144
|
+
('でででででbcで', 5, '…bcで', 5),
|
145
|
+
('でbcで', 3, '…で', 3),
|
146
|
+
('でbcで', 2, '…', 1),
|
147
|
+
('でbcで', 1, '…', 1),
|
148
|
+
('でbcで', 0, '', 0),
|
149
|
+
('でbcで', -1, '', 0),
|
150
|
+
])
|
151
|
+
def test_clipstr_start_truncator(self, s, w, clippeds, clippedw):
|
152
|
+
clips, clipw = visidata.clipstr_start(s, w, truncator='…')
|
153
|
+
assert clips == clippeds
|
154
|
+
assert clipw == clippedw
|
155
|
+
|
156
|
+
@pytest.mark.parametrize('s, w, clippeds, clippedw', [
|
157
|
+
('1234567890', 6, '12…890', 6),
|
158
|
+
('1234567890', 7, '123…890', 7),
|
159
|
+
('1234567890', 8, '123…7890', 8),
|
160
|
+
('1234567890', 9, '1234…7890', 9),
|
161
|
+
('1234567890', 10, '1234567890', 10),
|
162
|
+
('1234567890', 11, '1234567890', 10),
|
163
|
+
('1234567890', 99, '1234567890', 10),
|
164
|
+
# all full-width characters
|
165
|
+
('ででででで', 0, '', 0),
|
166
|
+
('ででででで', 1, '…', 1),
|
167
|
+
('ででででで', 2, '…', 1),
|
168
|
+
('ででででで', 3, '…で', 3),
|
169
|
+
('ででででで', 4, '…で', 3),
|
170
|
+
('ででででで', 5, 'で…で', 5),
|
171
|
+
('ででででで', 6, 'で…で', 5),
|
172
|
+
('ででででで', 7, 'で…でで', 7),
|
173
|
+
('ででででで', 8, 'で…でで', 7),
|
174
|
+
('ででででで', 9, 'でで…でで', 9),
|
175
|
+
('ででででで', 10, 'ででででで', 10),
|
176
|
+
('ででででで', 11, 'ででででで', 10),
|
177
|
+
('ででででで', 99, 'ででででで', 10),
|
178
|
+
# odd string length, with mix of full-width characters
|
179
|
+
('ででaaでa', 0, '', 0),
|
180
|
+
('ででaaでa', 1, '…', 1),
|
181
|
+
('ででaaでa', 2, '…a', 2),
|
182
|
+
('ででaaででa', 3, '…a', 2),
|
183
|
+
('ででaaででa', 4, '…でa', 4),
|
184
|
+
('ででaaででa', 5, 'で…a', 4),
|
185
|
+
('ででaaででa', 6, 'で…でa', 6),
|
186
|
+
])
|
187
|
+
def test_clipstr_middle(self, s, w, clippeds, clippedw):
|
188
|
+
clips, clipw = visidata.clipstr_middle(s, w)
|
189
|
+
assert clips == clippeds
|
190
|
+
assert clipw == clippedw
|
191
|
+
|
28
192
|
def test_clipdraw_chunks(self):
|
29
193
|
prechunks = [
|
30
194
|
('', 'x'),
|
visidata/tests/test_commands.py
CHANGED
@@ -30,6 +30,7 @@ nonTested = (
|
|
30
30
|
'breakpoint',
|
31
31
|
'redraw',
|
32
32
|
'menu',
|
33
|
+
'sysedit',
|
33
34
|
'sysopen',
|
34
35
|
'open-memusage',
|
35
36
|
)
|
@@ -67,6 +68,7 @@ inputLines = { 'save-sheet': 'jetsam.csv', # save to some tmp file
|
|
67
68
|
'addcol-incr-step': '2',
|
68
69
|
'setcol-incr-step': '2',
|
69
70
|
'setcol-iter': 'range(1, 100)',
|
71
|
+
'addcol-iter': 'range(1, 100)',
|
70
72
|
'setcol-format-enum': '1=cat',
|
71
73
|
'open-ping': 'github.com',
|
72
74
|
'setcol-input': '5',
|
@@ -116,6 +118,7 @@ inputLines = { 'save-sheet': 'jetsam.csv', # save to some tmp file
|
|
116
118
|
'sheet': '',
|
117
119
|
'col': 'Units',
|
118
120
|
'row': '5',
|
121
|
+
'addcol-aggregate': 'max',
|
119
122
|
}
|
120
123
|
|
121
124
|
@pytest.mark.usefixtures('curses_setup')
|
@@ -156,7 +159,8 @@ class TestCommands:
|
|
156
159
|
# cleanup
|
157
160
|
for f in ['flotsam.csv', 'debris.csv', 'jetsam.csv', 'lagan.csv', 'test_commands.vdj']:
|
158
161
|
pf = Path(f)
|
159
|
-
if pf.exists:
|
162
|
+
if pf.exists:
|
163
|
+
pf.unlink(missing_ok=True)
|
160
164
|
|
161
165
|
|
162
166
|
def runOneTest(self, mock_screen, longname):
|
@@ -172,7 +176,7 @@ class TestCommands:
|
|
172
176
|
vd.getkeystroke = Mock(side_effect=['^J'])
|
173
177
|
|
174
178
|
sample_file = vd.pkg_resources_files(visidata) / 'tests/sample.tsv'
|
175
|
-
vs = visidata.TsvSheet('
|
179
|
+
vs = visidata.TsvSheet('sample', source=visidata.Path(sample_file))
|
176
180
|
cmd = vs.getCommand(longname)
|
177
181
|
if not cmd:
|
178
182
|
vd.warning(f'command cannot be tested on TsvSheet, skipping: {longname}')
|
visidata/tests/test_menu.py
CHANGED
@@ -5,7 +5,7 @@ import pytest
|
|
5
5
|
|
6
6
|
class TestMenu:
|
7
7
|
def test_menuitems(self):
|
8
|
-
vd.addMenuItems('''Column > Add column > foobar >
|
8
|
+
vd.addMenuItems('''Column > Add column > foobar > addcol-sparkline''')
|
9
9
|
|
10
10
|
m = TableSheet().getMenuItem(['Column', 'Add column', 'foobar'])
|
11
11
|
assert m
|
visidata/textsheet.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import textwrap
|
2
|
+
import re
|
2
3
|
|
3
4
|
from visidata import vd, BaseSheet, options, Sheet, ColumnItem, asyncthread
|
4
5
|
from visidata import Column, vlen
|
@@ -59,9 +60,33 @@ class ErrorSheet(TextSheet):
|
|
59
60
|
ColumnItem('linenum', 0, type=int, width=0),
|
60
61
|
ColumnItem('error', 1),
|
61
62
|
]
|
62
|
-
guide = '''
|
63
|
+
guide = '''
|
64
|
+
# Error Sheet
|
65
|
+
This is the traceback for an error.
|
66
|
+
- move cursor then {help.commands.sysopen_error}
|
67
|
+
- `q` to quit this error sheet.
|
68
|
+
'''
|
63
69
|
precious = False
|
64
70
|
|
71
|
+
def sysopen_error(self, col, row):
|
72
|
+
'''Open an external editor for the file relevant to the cursor line
|
73
|
+
in the Error Sheet. If the cursor is on the first line, use the file
|
74
|
+
mentioned at the end of the stack trace'''
|
75
|
+
if self.rows and self.cursorRowIndex == 0:
|
76
|
+
searchidx = len(self.rows) - 1
|
77
|
+
else:
|
78
|
+
searchidx = self.cursorRowIndex
|
79
|
+
pat = re.compile(r'^ +File "(.*)", line (\d+), in ')
|
80
|
+
for _, text in self.rows[searchidx::-1]: # rowdef: [linenum, text]
|
81
|
+
match = pat.search(text)
|
82
|
+
if match:
|
83
|
+
vd.launchEditor(match.group(1), f'+{match.group(2)}')
|
84
|
+
return
|
85
|
+
|
86
|
+
def reload(self):
|
87
|
+
src = self.source or (vd.lastErrors[-1] if vd.lastErrors else [])
|
88
|
+
self.rows = list(enumerate(src))
|
89
|
+
|
65
90
|
class ErrorCellSheet(ErrorSheet):
|
66
91
|
columns = [
|
67
92
|
ColumnItem('linenum', 0, type=int, width=0),
|
@@ -69,6 +94,7 @@ class ErrorCellSheet(ErrorSheet):
|
|
69
94
|
]
|
70
95
|
guide = '''# Error Cell Sheet
|
71
96
|
This sheet shows the error that occurred when calculating a cell.
|
97
|
+
- move cursor then {help.commands.sysopen_error}
|
72
98
|
- `q` to quit this error sheet.
|
73
99
|
'''
|
74
100
|
|
@@ -79,29 +105,29 @@ class ErrorsSheet(Sheet):
|
|
79
105
|
ColumnItem('lastline', -1)
|
80
106
|
]
|
81
107
|
def reload(self):
|
82
|
-
self.rows = self.source
|
108
|
+
self.rows = self.source or vd.lastErrors
|
83
109
|
|
84
110
|
def openRow(self, row):
|
85
111
|
return ErrorSheet(source=self.cursorRow)
|
86
112
|
|
87
|
-
@VisiData.
|
113
|
+
@VisiData.lazy_property
|
88
114
|
def allErrorsSheet(self):
|
89
|
-
return ErrorsSheet("errors_all"
|
115
|
+
return ErrorsSheet("errors_all")
|
90
116
|
|
91
|
-
@VisiData.
|
117
|
+
@VisiData.lazy_property
|
92
118
|
def recentErrorsSheet(self):
|
93
|
-
|
94
|
-
return ErrorSheet("errors_recent", source=error)
|
119
|
+
return ErrorSheet("errors_recent")
|
95
120
|
|
96
121
|
|
97
122
|
|
98
|
-
BaseSheet.addCommand('^E', 'error-recent', '
|
123
|
+
BaseSheet.addCommand('^E', 'error-recent', 'recentErrorsSheet.reload(); vd.push(recentErrorsSheet) if vd.lastErrors else status("no error")', 'view traceback for most recent error')
|
99
124
|
BaseSheet.addCommand('g^E', 'errors-all', 'vd.push(vd.allErrorsSheet)', 'view traceback for most recent errors')
|
100
125
|
|
101
126
|
Sheet.addCommand('z^E', 'error-cell', 'vd.push(ErrorCellSheet(sheet.name+"_cell_error", sourceSheet=sheet, source=getattr(cursorCell, "error", None) or fail("no error this cell")))', 'view traceback for error in current cell')
|
102
127
|
|
103
128
|
TextSheet.addCommand('^O', 'sysopen-sheet', 'sheet.sysopen(sheet.cursorRowIndex)', 'open copy of text sheet in $EDITOR and reload on exit')
|
104
129
|
|
130
|
+
ErrorSheet.addCommand('Enter', 'sysopen-error', 'sysopen_error(cursorCol, cursorRow)', 'open traceback line in $EDITOR')
|
105
131
|
|
106
132
|
TextSheet.options.save_filetype = 'txt'
|
107
133
|
|
visidata/themes/ascii8.py
CHANGED
@@ -82,6 +82,6 @@ vd.themes['ascii8'] = dict(
|
|
82
82
|
disp_menu_fmt='7-bit ASCII 3-bit color',
|
83
83
|
plot_colors = 'white',
|
84
84
|
disp_histogram='*',
|
85
|
-
|
86
|
-
|
85
|
+
disp_graph_reflines_x_charset='||||',
|
86
|
+
disp_graph_reflines_y_charset='----'
|
87
87
|
)
|
visidata/themes/light.py
CHANGED
@@ -4,9 +4,14 @@ from visidata import vd
|
|
4
4
|
|
5
5
|
vd.themes['light'] = dict(
|
6
6
|
color_default = 'black on white', # the default fg and bg colors
|
7
|
+
color_default_hdr = 'bold black on white',
|
8
|
+
color_bottom_hdr = 'underline black on white',
|
9
|
+
color_current_col = 'bold on white',
|
10
|
+
color_column_sep = 'black on white',
|
7
11
|
color_key_col = '20 blue', # color of key columns
|
8
12
|
color_edit_cell = '234 black', # cell color to use when editing cell
|
9
13
|
color_selected_row = '164 magenta', # color of selected rows
|
14
|
+
color_selected_col = 'bold on white',
|
10
15
|
color_note_row = '164 magenta', # color of row note on left edge
|
11
16
|
color_note_type = '88 red', # color of cell note for non-str types in anytype columns
|
12
17
|
color_warning = '202 11 yellow',
|
visidata/threads.py
CHANGED
@@ -270,6 +270,27 @@ def asyncsingle(func):
|
|
270
270
|
_execAsync.searchThread = None
|
271
271
|
return _execAsync
|
272
272
|
|
273
|
+
def asyncsingle_queue(func):
|
274
|
+
'''Function decorator like `@asyncthread` but as a singleton. When called, `func(...)` spawns a new thread, and waits for the end of any previous thread still running *func*.
|
275
|
+
``vd.sync()`` does wait for unfinished asyncsingle_queue threads, which is an important difference from asyncsingle.
|
276
|
+
'''
|
277
|
+
@functools.wraps(func)
|
278
|
+
def _execAsync(*args, **kwargs):
|
279
|
+
def _func(*args, **kwargs):
|
280
|
+
func(*args, **kwargs)
|
281
|
+
_execAsync.searchThread = None
|
282
|
+
# end of thread
|
283
|
+
|
284
|
+
# cancel previous thread if running
|
285
|
+
if _execAsync.searchThread:
|
286
|
+
vd.sync(_execAsync.searchThread)
|
287
|
+
|
288
|
+
_func.__name__ = func.__name__ # otherwise, the the thread's name is '_func'
|
289
|
+
|
290
|
+
_execAsync.searchThread = vd.execAsync(_func, *args, **kwargs)
|
291
|
+
_execAsync.searchThread = None
|
292
|
+
return _execAsync
|
293
|
+
|
273
294
|
@VisiData.property
|
274
295
|
def unfinishedThreads(self):
|
275
296
|
'A list of unfinished threads (those without a recorded `endTime`).'
|
@@ -319,13 +340,14 @@ def open_pyprof(vd, p):
|
|
319
340
|
@VisiData.api
|
320
341
|
def toggleProfiling(vd):
|
321
342
|
t = threading.current_thread()
|
322
|
-
if not
|
323
|
-
t.profile
|
343
|
+
if not vd.options.profile:
|
344
|
+
if not t.profile:
|
345
|
+
t.profile = cProfile.Profile()
|
324
346
|
t.profile.enable()
|
325
|
-
|
326
|
-
vd.options.set('profile', True)
|
347
|
+
vd.options.set('profile', True)
|
327
348
|
else:
|
328
|
-
t.profile
|
349
|
+
if t.profile:
|
350
|
+
t.profile.disable()
|
329
351
|
vd.options.set('profile', False)
|
330
352
|
vd.status('profiling ' + ('ON' if vd.options.profile else 'OFF'))
|
331
353
|
|
@@ -337,7 +359,10 @@ class ThreadProfiler:
|
|
337
359
|
|
338
360
|
def __enter__(self):
|
339
361
|
if vd.options.profile:
|
340
|
-
|
362
|
+
try:
|
363
|
+
self.thread.profile.enable()
|
364
|
+
except ValueError: #"ValueError: Another profiling tool is already active"
|
365
|
+
pass
|
341
366
|
return self
|
342
367
|
|
343
368
|
def __exit__(self, exc_type, exc_val, tb):
|
@@ -381,6 +406,7 @@ class ProfileSheet(Sheet):
|
|
381
406
|
]
|
382
407
|
|
383
408
|
nKeys=3
|
409
|
+
_ordering = [('inlinetime_us', True)] # initially sort by inlinetime descending
|
384
410
|
|
385
411
|
def reload(self):
|
386
412
|
if isinstance(self.source, cProfile.Profile):
|
@@ -388,7 +414,6 @@ class ProfileSheet(Sheet):
|
|
388
414
|
else:
|
389
415
|
self.rows = self.source
|
390
416
|
|
391
|
-
self.orderBy(None, self.column('inlinetime_us'), reverse=True)
|
392
417
|
self.callers = collections.defaultdict(list) # [row.code] -> list(code)
|
393
418
|
|
394
419
|
for r in self.rows:
|
@@ -435,6 +460,10 @@ def codestr(code):
|
|
435
460
|
return code.co_name
|
436
461
|
|
437
462
|
|
463
|
+
@VisiData.lazy_property
|
464
|
+
def allThreadsSheet(self):
|
465
|
+
return ThreadsSheet("threads", source=vd.threads)
|
466
|
+
|
438
467
|
ThreadsSheet.addCommand('^C', 'cancel-thread', 'cancelThread(cursorRow)', 'abort thread at current row')
|
439
468
|
ThreadsSheet.addCommand('g^C', 'cancel-all', 'cancelThread(*sheet.rows)', 'abort all threads on this threads sheet')
|
440
469
|
ThreadsSheet.addCommand(None, 'add-row', 'fail("cannot add new rows on Threads Sheet")', 'invalid command')
|
@@ -449,7 +478,7 @@ BaseSheet.addCommand('^C', 'cancel-sheet', 'cancelThread(*sheet.currentThreads o
|
|
449
478
|
BaseSheet.addCommand('g^C', 'cancel-all', 'liveThreads=list(t for vs in vd.sheets for t in vs.currentThreads); cancelThread(*liveThreads); status("canceled %s threads" % len(liveThreads))', 'abort all spawned threads')
|
450
479
|
|
451
480
|
|
452
|
-
BaseSheet.addCommand('^T', 'threads-all', 'vd.push(
|
481
|
+
BaseSheet.addCommand('^T', 'threads-all', 'vd.push(vd.allThreadsSheet)', 'open Threads for all sheets')
|
453
482
|
BaseSheet.addCommand('z^T', 'threads-sheet', 'vd.push(ThreadsSheet("threads", source=sheet.currentThreads))', 'open Threads for this sheet')
|
454
483
|
|
455
484
|
vd.addGlobals({
|
@@ -457,6 +486,7 @@ vd.addGlobals({
|
|
457
486
|
'Progress': Progress,
|
458
487
|
'asynccache': asynccache,
|
459
488
|
'asyncsingle': asyncsingle,
|
489
|
+
'asyncsingle_queue': asyncsingle_queue,
|
460
490
|
'asyncignore': asyncignore,
|
461
491
|
})
|
462
492
|
|
visidata/utils.py
CHANGED
@@ -5,7 +5,7 @@ import re
|
|
5
5
|
|
6
6
|
'Various helper classes and functions.'
|
7
7
|
|
8
|
-
__all__ = ['AlwaysDict', 'AttrDict', 'DefaultAttrDict', 'moveListItem', 'namedlist', 'classproperty', 'MissingAttrFormatter', 'getitem', 'setitem', 'getitemdef', 'getitemdeep', 'setitemdeep', 'getattrdeep', 'setattrdeep', 'ExplodingMock', 'ScopedSetattr']
|
8
|
+
__all__ = ['AlwaysDict', 'AttrDict', 'DefaultAttrDict', 'moveListItem', 'namedlist', 'classproperty', 'MissingAttrFormatter', 'getitem', 'setitem', 'getitemdef', 'getitemdeep', 'setitemdeep', 'getattrdeep', 'setattrdeep', 'ExplodingMock', 'ScopedSetattr', 'colname_letters']
|
9
9
|
|
10
10
|
|
11
11
|
class AlwaysDict(dict):
|
@@ -215,3 +215,17 @@ def ScopedSetattr(obj, attrname, val):
|
|
215
215
|
yield
|
216
216
|
finally:
|
217
217
|
setattr(obj, attrname, oldval)
|
218
|
+
|
219
|
+
def colname_letters(num):
|
220
|
+
'''*num* is a 1-based integer: 1, 2, 3... gives A B C .. Z AA AB .. ZZ AAA .. to infinity; *num* of 0 returns the empty string'''
|
221
|
+
# credit to https://stackoverflow.com/questions/48983939/convert-a-number-to-excel-s-base-26/48984697#48984697
|
222
|
+
def divmod_excel(n):
|
223
|
+
a, b = divmod(n, 26)
|
224
|
+
if b == 0:
|
225
|
+
return a - 1, b + 26
|
226
|
+
return a, b
|
227
|
+
chars = []
|
228
|
+
while num > 0:
|
229
|
+
num, d = divmod_excel(num)
|
230
|
+
chars.append('-ABCDEFGHIJKLMNOPQRSTUVWXYZ'[d])
|
231
|
+
return ''.join(reversed(chars))
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
.Dd
|
1
|
+
.Dd June 13, 2025
|
2
2
|
.Dt vd \&1 "Quick Reference Guide"
|
3
3
|
.Os Linux/MacOS
|
4
4
|
.
|
@@ -193,6 +193,8 @@ adjust widths of all visible columns to Ar number
|
|
193
193
|
.Pp
|
194
194
|
.It Ic " -" Ns " (hyphen)"
|
195
195
|
hide current column
|
196
|
+
.It Ic "g-" Ns " (hyphen)"
|
197
|
+
hide any column that has multiple rows but only one distinct value
|
196
198
|
.It Ic "z-" Ns
|
197
199
|
reduce width of current column by half
|
198
200
|
.It Ic "gv" Ns
|
@@ -247,10 +249,10 @@ add/reset cache for current/all visible column(s)
|
|
247
249
|
.No add new columns from capture groups of Ar regex No (also requires example row)
|
248
250
|
.It Ic "z" Ns Ic "\&;" Ar expr
|
249
251
|
.No create new column from bash Ar expr Ns , with Sy $ Ns columnNames as variables
|
250
|
-
.It Ic " *" Ar
|
251
|
-
.No add column derived from current column, replacing Ar
|
252
|
-
.It Ic "g* gz*" Ar
|
253
|
-
.No modify selected rows in current/all visible column(s), replacing Ar
|
252
|
+
.It Ic " *" Ar search No Sy Tab No Ar replace
|
253
|
+
.No add column derived from current column, replacing Ar search No regex with Ar replace No (may include Sy \e1 No backrefs)
|
254
|
+
.It Ic "g* gz*" Ar search No Sy Tab No Ar replace
|
255
|
+
.No modify selected rows in current/all visible column(s), replacing Ar search No with Ar replace No (may include Sy \e1 No backrefs)
|
254
256
|
.Pp
|
255
257
|
.It Ic " ( g("
|
256
258
|
.No expand current/all visible column(s) of lists (e.g. Sy [3] Ns ) or dicts (e.g. Sy {3} Ns ) one level
|
@@ -300,7 +302,7 @@ sort ascending/descending by current column; replace any existing sort criteria
|
|
300
302
|
.It Ic " g[ g]"
|
301
303
|
sort ascending/descending by all key columns; replace any existing sort criteria
|
302
304
|
.It Ic " z[ z]"
|
303
|
-
sort ascending/descending by current column;
|
305
|
+
sort ascending/descending by current column; keep higher priority sort criteria
|
304
306
|
.It Ic "gz[ gz]"
|
305
307
|
sort ascending/descending by all key columns; add to existing sort criteria
|
306
308
|
.It Ic " \&""
|
@@ -316,6 +318,8 @@ open duplicate sheet with deepcopy of selected rows
|
|
316
318
|
The rows in these duplicated sheets (except deepcopy) are references to rows on the original source sheets, and so edits to the filtered rows will naturally be reflected in the original rows. Use
|
317
319
|
.Ic "g'"
|
318
320
|
to freeze sheet contents in a deliberate copy.
|
321
|
+
.Ic "z'"
|
322
|
+
replace current column with a frozen copy, with all cells evaluated
|
319
323
|
.
|
320
324
|
.Ss Editing Rows and Cells
|
321
325
|
.
|
@@ -352,6 +356,8 @@ fill null cells in current column with contents of non-null cells up the current
|
|
352
356
|
.
|
353
357
|
.It Ic " e" Ar text
|
354
358
|
edit contents of current cell
|
359
|
+
.It Ic " ^O"
|
360
|
+
.No edit contents of current cell in external Sy EDITOR
|
355
361
|
.It Ic " ge" Ar text
|
356
362
|
.No set contents of current column for selected rows to Ar text
|
357
363
|
.
|
@@ -443,6 +449,8 @@ increase/decrease zoom level, centered on cursor
|
|
443
449
|
zoom to fit full extent
|
444
450
|
.It Ic "z_" No (underbar)
|
445
451
|
set aspect ratio
|
452
|
+
.It Ic "g_" No (underbar)
|
453
|
+
Zoom y-axis to fit all visible data points
|
446
454
|
.It Ic " x" Ar xmin xmax
|
447
455
|
.No set Ar xmin Ns / Ns Ar xmax No on graph
|
448
456
|
.It Ic " y" Ar ymin ymax
|
@@ -782,7 +790,7 @@ abort replay
|
|
782
790
|
.It Ic ^T
|
783
791
|
.No open global Sy Threads Sheet No for all asynchronous threads running
|
784
792
|
.It Ic z^T
|
785
|
-
.No open current sheet's Sy Threads Sheet
|
793
|
+
.No open current sheet's Sy Threads Sheet
|
786
794
|
.El
|
787
795
|
.Bl -inset -compact
|
788
796
|
.It (sheet-specific commands)
|
@@ -791,7 +799,7 @@ abort replay
|
|
791
799
|
.It Ic " ^C"
|
792
800
|
abort thread at current row
|
793
801
|
.It Ic "g^C"
|
794
|
-
.No abort all threads on current Sy Threads Sheet
|
802
|
+
.No abort all threads on current Sy Threads Sheet
|
795
803
|
.El
|
796
804
|
.
|
797
805
|
.Ss DERIVED SHEETS
|
@@ -912,7 +920,7 @@ disable loading .visidatarc and plugin addons
|
|
912
920
|
.
|
913
921
|
.El
|
914
922
|
.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -compact
|
915
|
-
.It Sy --visidata-dir Ns = Ns Ar "str " No "
|
923
|
+
.It Sy --visidata-dir Ns = Ns Ar "str " No "/home/anja/.config/visidata"
|
916
924
|
directory to load and store additional files
|
917
925
|
.It Sy --debug No " False"
|
918
926
|
exit on error and display stacktrace
|
@@ -1021,7 +1029,7 @@ source of randomized startup messages
|
|
1021
1029
|
folder recursion depth on DirSheet
|
1022
1030
|
.It Sy --dir-hidden No " False"
|
1023
1031
|
load hidden files on DirSheet
|
1024
|
-
.It Sy --config Ns = Ns Ar "Path " No "/home/
|
1032
|
+
.It Sy --config Ns = Ns Ar "Path " No "/home/anja/.visidatarc"
|
1025
1033
|
config file to exec in Python
|
1026
1034
|
.It Sy --play Ns = Ns Ar "str " No ""
|
1027
1035
|
file.vdj to replay
|
@@ -1055,7 +1063,7 @@ device ID associated with matrix login
|
|
1055
1063
|
client_id for reddit api
|
1056
1064
|
.It Sy --reddit-client-secret Ns = Ns Ar "str " No ""
|
1057
1065
|
client_secret for reddit api
|
1058
|
-
.It Sy --reddit-user-agent Ns = Ns Ar "str " No "3.
|
1066
|
+
.It Sy --reddit-user-agent Ns = Ns Ar "str " No "3.3"
|
1059
1067
|
user_agent for reddit api
|
1060
1068
|
.It Sy --zulip-batch-size Ns = Ns Ar "int " No "-100"
|
1061
1069
|
number of messages to fetch per call (<0 to fetch before anchor)
|
@@ -1100,6 +1108,8 @@ max number of fixed-width columns to create (0 is no max)
|
|
1100
1108
|
whether to include edge labels on graphviz diagrams
|
1101
1109
|
.It Sy --grep-base-dir Ns = Ns Ar "NoneType " No "None"
|
1102
1110
|
base directory for relative paths opened with sysopen-row
|
1111
|
+
.It Sy --hdf5-matrix-enumerate No " False"
|
1112
|
+
enumerate matrix rows and columns
|
1103
1113
|
.It Sy --html-title Ns = Ns Ar "str " No "<h2>{sheet.name}</h2>"
|
1104
1114
|
table header when saving to html
|
1105
1115
|
.It Sy --http-max-next Ns = Ns Ar "int " No "0"
|
@@ -1110,6 +1120,8 @@ http headers to send to requests
|
|
1110
1120
|
verify host and certificates for https
|
1111
1121
|
.It Sy --npy-allow-pickle No " False"
|
1112
1122
|
numpy allow unpickling objects (unsafe)
|
1123
|
+
.It Sy --npy-matrix-enumerate No " False"
|
1124
|
+
enumerate matrix rows and columns
|
1113
1125
|
.It Sy --pcap-internet Ns = Ns Ar "str " No "n"
|
1114
1126
|
(y/s/n) if save_dot includes all internet hosts separately (y), combined (s), or does not include the internet (n)
|
1115
1127
|
.It Sy --pdf-tables No " False"
|
@@ -1144,8 +1156,6 @@ API Key for api.apilayer.com/fixer
|
|
1144
1156
|
Cache days for currency conversions
|
1145
1157
|
.It Sy --describe-aggrs Ns = Ns Ar "str " No "mean stdev"
|
1146
1158
|
numeric aggregators to calculate on Describe sheet
|
1147
|
-
.It Sy --hello-world Ns = Ns Ar "str " No "\[u00A1]Hola mundo!"
|
1148
|
-
shown by the hello-world command
|
1149
1159
|
.It Sy --incr-base Ns = Ns Ar "float " No "1.0"
|
1150
1160
|
start value for column increments
|
1151
1161
|
.It Sy --ping-count Ns = Ns Ar "int " No "3"
|
@@ -1193,7 +1203,7 @@ command submenu indicator
|
|
1193
1203
|
indicator if command pushes sheet onto sheet stack
|
1194
1204
|
.It Sy "disp_menu_input " No "\[u2026]"
|
1195
1205
|
indicator if input required for command
|
1196
|
-
.It Sy "disp_menu_fmt " No "| VisiData {vd.version} | {vd.
|
1206
|
+
.It Sy "disp_menu_fmt " No "| VisiData {vd.version} | {vd.motd}"
|
1197
1207
|
right-side menu format string
|
1198
1208
|
.It Sy "disp_float_fmt " No "{:.02f}"
|
1199
1209
|
default fmtstr to format float values
|
@@ -1305,6 +1315,8 @@ replace whitespace with spaces in multiline
|
|
1305
1315
|
multiline string to indicate truncation
|
1306
1316
|
.It Sy "disp_multiline_focus" No "True"
|
1307
1317
|
only multiline cursor row
|
1318
|
+
.It Sy "color_multiline_bottom" No ""
|
1319
|
+
color of bottom line of multiline rows
|
1308
1320
|
.It Sy "color_aggregator " No "bold 255 white on 234 black"
|
1309
1321
|
color of aggregator summary on bottom row
|
1310
1322
|
.It Sy "disp_rstatus_fmt " No "{sheet.threadStatus} {sheet.keystrokeStatus} [:longname_status]{sheet.longname}[/] {sheet.nRows:9d} {sheet.rowtype} {sheet.modifiedStatus}{sheet.selectedStatus}{vd.replayStatus}{vd.sidebarStatus}"
|
@@ -1365,7 +1377,7 @@ histogram element character
|
|
1365
1377
|
show axes and legend on graph
|
1366
1378
|
.It Sy "disp_canvas_charset" No "\[u2800]\[u2801]\[u2802]\[u2803]\[u2804]\[u2805]\[u2806]\[u2807]\[u2808]\[u2809]\[u280A]\[u280B]\[u280C]\[u280D]\[u280E]\[u280F]\[u2810]\[u2811]\[u2812]\[u2813]\[u2814]\[u2815]\[u2816]\[u2817]\[u2818]\[u2819]\[u281A]\[u281B]\[u281C]\[u281D]\[u281E]\[u281F]\[u2820]\[u2821]\[u2822]\[u2823]\[u2824]\[u2825]\[u2826]\[u2827]\[u2828]\[u2829]\[u282A]\[u282B]\[u282C]\[u282D]\[u282E]\[u282F]\[u2830]\[u2831]\[u2832]\[u2833]\[u2834]\[u2835]\[u2836]\[u2837]\[u2838]\[u2839]\[u283A]\[u283B]\[u283C]\[u283D]\[u283E]\[u283F]\[u2840]\[u2841]\[u2842]\[u2843]\[u2844]\[u2845]\[u2846]\[u2847]\[u2848]\[u2849]\[u284A]\[u284B]\[u284C]\[u284D]\[u284E]\[u284F]\[u2850]\[u2851]\[u2852]\[u2853]\[u2854]\[u2855]\[u2856]\[u2857]\[u2858]\[u2859]\[u285A]\[u285B]\[u285C]\[u285D]\[u285E]\[u285F]\[u2860]\[u2861]\[u2862]\[u2863]\[u2864]\[u2865]\[u2866]\[u2867]\[u2868]\[u2869]\[u286A]\[u286B]\[u286C]\[u286D]\[u286E]\[u286F]\[u2870]\[u2871]\[u2872]\[u2873]\[u2874]\[u2875]\[u2876]\[u2877]\[u2878]\[u2879]\[u287A]\[u287B]\[u287C]\[u287D]\[u287E]\[u287F]\[u2880]\[u2881]\[u2882]\[u2883]\[u2884]\[u2885]\[u2886]\[u2887]\[u2888]\[u2889]\[u288A]\[u288B]\[u288C]\[u288D]\[u288E]\[u288F]\[u2890]\[u2891]\[u2892]\[u2893]\[u2894]\[u2895]\[u2896]\[u2897]\[u2898]\[u2899]\[u289A]\[u289B]\[u289C]\[u289D]\[u289E]\[u289F]\[u28A0]\[u28A1]\[u28A2]\[u28A3]\[u28A4]\[u28A5]\[u28A6]\[u28A7]\[u28A8]\[u28A9]\[u28AA]\[u28AB]\[u28AC]\[u28AD]\[u28AE]\[u28AF]\[u28B0]\[u28B1]\[u28B2]\[u28B3]\[u28B4]\[u28B5]\[u28B6]\[u28B7]\[u28B8]\[u28B9]\[u28BA]\[u28BB]\[u28BC]\[u28BD]\[u28BE]\[u28BF]\[u28C0]\[u28C1]\[u28C2]\[u28C3]\[u28C4]\[u28C5]\[u28C6]\[u28C7]\[u28C8]\[u28C9]\[u28CA]\[u28CB]\[u28CC]\[u28CD]\[u28CE]\[u28CF]\[u28D0]\[u28D1]\[u28D2]\[u28D3]\[u28D4]\[u28D5]\[u28D6]\[u28D7]\[u28D8]\[u28D9]\[u28DA]\[u28DB]\[u28DC]\[u28DD]\[u28DE]\[u28DF]\[u28E0]\[u28E1]\[u28E2]\[u28E3]\[u28E4]\[u28E5]\[u28E6]\[u28E7]\[u28E8]\[u28E9]\[u28EA]\[u28EB]\[u28EC]\[u28ED]\[u28EE]\[u28EF]\[u28F0]\[u28F1]\[u28F2]\[u28F3]\[u28F4]\[u28F5]\[u28F6]\[u28F7]\[u28F8]\[u28F9]\[u28FA]\[u28FB]\[u28FC]\[u28FD]\[u28FE]\[u28FF]"
|
1367
1379
|
charset to render 2x4 blocks on canvas
|
1368
|
-
.It Sy "
|
1380
|
+
.It Sy "disp_graph_pixel_random" No "False"
|
1369
1381
|
randomly choose attr from set of pixels instead of most common
|
1370
1382
|
.It Sy "disp_zoom_incr " No "2.0"
|
1371
1383
|
amount to multiply current zoomlevel when zooming
|
@@ -1385,8 +1397,17 @@ charset to render vertical reference lines on graph
|
|
1385
1397
|
charset to render horizontal reference lines on graph
|
1386
1398
|
.It Sy "disp_graph_multiple_reflines_char" No "\[u2592]"
|
1387
1399
|
char to render multiple parallel reflines
|
1388
|
-
.It Sy "
|
1389
|
-
|
1400
|
+
.It Sy "disp_help_flags " No "cmdpalette guides help hints inputfield inputkeys nometacols sidebar"
|
1401
|
+
list of helper features to enable (space-separated):
|
1402
|
+
- "cmdpalette": exec-longname suggestions
|
1403
|
+
- "guides": guides in sidebar
|
1404
|
+
- "help": help sidebar collapsed by default
|
1405
|
+
- "hints": context-sensitive hints on menu line
|
1406
|
+
- "inputfield": context-sensitive help for each input field
|
1407
|
+
- "inputkeys": input quick reference in sidebar
|
1408
|
+
- "nometacols": hide expert columns on metasheets
|
1409
|
+
- "sidebar": context-sensitive sheet help in sidebar
|
1410
|
+
- "all": enable all helper features
|
1390
1411
|
.It Sy "color_add_pending " No "green"
|
1391
1412
|
color for rows pending add
|
1392
1413
|
.It Sy "color_change_pending" No "reverse yellow"
|