visidata 2.11.dev0__py3-none-any.whl → 3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- visidata/__init__.py +72 -91
- visidata/_input.py +263 -44
- visidata/_open.py +84 -29
- visidata/_types.py +22 -4
- visidata/_urlcache.py +17 -4
- visidata/aggregators.py +65 -25
- visidata/apps/__init__.py +0 -0
- visidata/apps/vdsql/__about__.py +8 -0
- visidata/apps/vdsql/__init__.py +5 -0
- visidata/apps/vdsql/__main__.py +27 -0
- visidata/apps/vdsql/_ibis.py +748 -0
- visidata/apps/vdsql/bigquery.py +61 -0
- visidata/apps/vdsql/clickhouse.py +53 -0
- visidata/apps/vdsql/setup.py +40 -0
- visidata/apps/vdsql/snowflake.py +67 -0
- visidata/apps/vgit/__init__.py +13 -0
- visidata/apps/vgit/__main__.py +3 -0
- visidata/apps/vgit/abort.py +23 -0
- visidata/apps/vgit/blame.py +76 -0
- visidata/apps/vgit/branch.py +153 -0
- visidata/apps/vgit/config.py +95 -0
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- visidata/apps/vgit/grep.py +37 -0
- visidata/apps/vgit/log.py +81 -0
- visidata/apps/vgit/main.py +55 -0
- visidata/apps/vgit/remote.py +57 -0
- visidata/apps/vgit/repos.py +71 -0
- visidata/apps/vgit/setup.py +37 -0
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- visidata/apps/vgit/statusbar.py +34 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +251 -99
- visidata/choose.py +15 -11
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +84 -18
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +89 -114
- visidata/color.py +142 -56
- visidata/column.py +134 -131
- visidata/ddw/input.ddw +74 -79
- visidata/ddw/regex.ddw +57 -0
- visidata/ddwplay.py +33 -14
- visidata/deprecated.py +77 -3
- visidata/desktop/visidata.desktop +7 -0
- visidata/editor.py +12 -6
- visidata/errors.py +5 -1
- visidata/experimental/__init__.py +0 -0
- visidata/experimental/diff_sheet.py +29 -0
- visidata/experimental/digit_autoedit.py +6 -0
- visidata/experimental/gdrive.py +89 -0
- visidata/experimental/google.py +37 -0
- visidata/experimental/gsheets.py +79 -0
- visidata/experimental/live_search.py +37 -0
- visidata/experimental/liveupdate.py +45 -0
- visidata/experimental/mark.py +133 -0
- visidata/experimental/noahs_tapestry/__init__.py +1 -0
- visidata/experimental/noahs_tapestry/tapestry.py +147 -0
- visidata/experimental/rownum.py +73 -0
- visidata/experimental/slide_cells.py +26 -0
- visidata/expr.py +8 -4
- visidata/extensible.py +32 -6
- visidata/features/__init__.py +0 -0
- visidata/features/addcol_audiometadata.py +42 -0
- visidata/features/addcol_histogram.py +34 -0
- visidata/features/canvas_save_svg.py +69 -0
- visidata/features/change_precision.py +46 -0
- visidata/features/cmdpalette.py +163 -0
- visidata/features/colorbrewer.py +363 -0
- visidata/{colorsheet.py → features/colorsheet.py} +17 -16
- visidata/features/command_server.py +105 -0
- visidata/features/currency_to_usd.py +70 -0
- visidata/{customdate.py → features/customdate.py} +2 -0
- visidata/features/dedupe.py +132 -0
- visidata/{describe.py → features/describe.py} +17 -15
- visidata/features/errors_guide.py +26 -0
- visidata/features/expand_cols.py +202 -0
- visidata/{fill.py → features/fill.py} +4 -2
- visidata/{freeze.py → features/freeze.py} +11 -6
- visidata/features/graph_seaborn.py +79 -0
- visidata/features/helloworld.py +10 -0
- visidata/features/hint_types.py +17 -0
- visidata/{incr.py → features/incr.py} +5 -0
- visidata/{join.py → features/join.py} +107 -53
- visidata/features/known_cols.py +21 -0
- visidata/features/layout.py +62 -0
- visidata/{melt.py → features/melt.py} +33 -21
- visidata/features/normcol.py +118 -0
- visidata/features/open_config.py +7 -0
- visidata/features/open_syspaste.py +18 -0
- visidata/features/ping.py +157 -0
- visidata/features/procmgr.py +208 -0
- visidata/features/random_sample.py +6 -0
- visidata/{regex.py → features/regex.py} +47 -31
- visidata/features/reload_every.py +55 -0
- visidata/features/rename_col_cascade.py +30 -0
- visidata/features/scroll_context.py +60 -0
- visidata/features/select_equal_selected.py +11 -0
- visidata/features/setcol_fake.py +65 -0
- visidata/{slide.py → features/slide.py} +75 -21
- visidata/features/sparkline.py +48 -0
- visidata/features/status_source.py +20 -0
- visidata/{sysedit.py → features/sysedit.py} +2 -1
- visidata/features/sysopen_mailcap.py +46 -0
- visidata/features/term_extras.py +13 -0
- visidata/{transpose.py → features/transpose.py} +5 -4
- visidata/features/type_ipaddr.py +73 -0
- visidata/features/type_url.py +11 -0
- visidata/{unfurl.py → features/unfurl.py} +9 -9
- visidata/{window.py → features/window.py} +2 -2
- visidata/form.py +50 -21
- visidata/freqtbl.py +81 -33
- visidata/fuzzymatch.py +414 -0
- visidata/graph.py +105 -33
- visidata/guide.py +180 -0
- visidata/help.py +75 -44
- visidata/hint.py +39 -0
- visidata/indexsheet.py +109 -0
- visidata/input_history.py +55 -0
- visidata/interface.py +58 -0
- visidata/keys.py +17 -16
- visidata/loaders/__init__.py +9 -0
- visidata/loaders/_pandas.py +61 -21
- visidata/loaders/api_airtable.py +70 -0
- visidata/loaders/api_bitio.py +102 -0
- visidata/loaders/api_matrix.py +148 -0
- visidata/loaders/api_reddit.py +306 -0
- visidata/loaders/api_zulip.py +249 -0
- visidata/loaders/archive.py +41 -7
- visidata/loaders/arrow.py +7 -7
- visidata/loaders/conll.py +49 -0
- visidata/loaders/csv.py +25 -7
- visidata/loaders/eml.py +3 -4
- visidata/loaders/f5log.py +1204 -0
- visidata/loaders/fec.py +325 -0
- visidata/loaders/fixed_width.py +3 -5
- visidata/loaders/frictionless.py +3 -3
- visidata/loaders/geojson.py +8 -5
- visidata/loaders/google.py +48 -0
- visidata/loaders/graphviz.py +4 -4
- visidata/loaders/hdf5.py +4 -4
- visidata/loaders/html.py +48 -10
- visidata/loaders/http.py +84 -30
- visidata/loaders/imap.py +20 -10
- visidata/loaders/jrnl.py +52 -0
- visidata/loaders/json.py +83 -29
- visidata/loaders/jsonla.py +74 -0
- visidata/loaders/lsv.py +15 -11
- visidata/loaders/mailbox.py +40 -0
- visidata/loaders/markdown.py +1 -3
- visidata/loaders/mbtiles.py +4 -5
- visidata/loaders/mysql.py +11 -13
- visidata/loaders/npy.py +7 -7
- visidata/loaders/odf.py +4 -1
- visidata/loaders/orgmode.py +428 -0
- visidata/loaders/pandas_freqtbl.py +14 -20
- visidata/loaders/parquet.py +62 -6
- visidata/loaders/pcap.py +3 -3
- visidata/loaders/pdf.py +4 -3
- visidata/loaders/png.py +19 -13
- visidata/loaders/postgres.py +9 -8
- visidata/loaders/rec.py +7 -3
- visidata/loaders/s3.py +342 -0
- visidata/loaders/sas.py +5 -5
- visidata/loaders/scrape.py +186 -0
- visidata/loaders/shp.py +6 -5
- visidata/loaders/spss.py +5 -6
- visidata/loaders/sqlite.py +68 -28
- visidata/loaders/texttables.py +1 -1
- visidata/loaders/toml.py +60 -0
- visidata/loaders/tsv.py +61 -19
- visidata/loaders/ttf.py +19 -7
- visidata/loaders/unzip_http.py +6 -5
- visidata/loaders/usv.py +1 -1
- visidata/loaders/vcf.py +16 -16
- visidata/loaders/vds.py +10 -7
- visidata/loaders/vdx.py +30 -5
- visidata/loaders/xlsb.py +8 -1
- visidata/loaders/xlsx.py +145 -25
- visidata/loaders/xml.py +6 -3
- visidata/loaders/xword.py +4 -4
- visidata/loaders/yaml.py +15 -5
- visidata/macos.py +1 -1
- visidata/macros.py +130 -41
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -154
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +302 -147
- visidata/man/vd.txt +291 -151
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +79 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +131 -43
- visidata/pivot.py +74 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +42 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -24
- visidata/sheets.py +229 -257
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +113 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +62 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +17 -0
- visidata/tests/test_menu.py +14 -0
- visidata/tests/test_path.py +13 -4
- visidata/text_source.py +53 -0
- visidata/textsheet.py +10 -3
- visidata/theme.py +44 -0
- visidata/themes/__init__.py +0 -0
- visidata/themes/ascii8.py +84 -0
- visidata/themes/asciimono.py +84 -0
- visidata/themes/light.py +17 -0
- visidata/threads.py +87 -39
- visidata/tuiwin.py +22 -0
- visidata/type_currency.py +22 -3
- visidata/type_date.py +31 -9
- visidata/type_floatsi.py +5 -1
- visidata/undo.py +18 -6
- visidata/utils.py +106 -23
- visidata/vdobj.py +28 -17
- visidata/windows.py +10 -0
- visidata/wrappers.py +9 -3
- visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/vd.1 +302 -147
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +302 -147
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/METADATA +13 -11
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -1
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.dev0.dist-info/RECORD +0 -142
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
from visidata import vd, VisiData, ItemColumn, AttrDict
|
2
|
+
|
3
|
+
|
4
|
+
from .gitsheet import GitSheet
|
5
|
+
from .diff import GitDiffSheet
|
6
|
+
|
7
|
+
|
8
|
+
@VisiData.api
|
9
|
+
def git_stash(vd, p, args):
|
10
|
+
if 'list' in args:
|
11
|
+
return GitStashes('git-stash-list', source=p, gitargs=args)
|
12
|
+
|
13
|
+
|
14
|
+
class GitStashes(GitSheet):
|
15
|
+
guide = '''
|
16
|
+
# git stash
|
17
|
+
This is the list of changes that have been stashed previously.
|
18
|
+
|
19
|
+
`a` to apply this stashed change (without removing it)
|
20
|
+
`d` to drop this stashed change
|
21
|
+
`b` to create a branch from this stashed change'),
|
22
|
+
'''
|
23
|
+
rowtype = 'stashed change' # rowdef: AttrDict(stashid=, branched_from=, sha1=, msg=)
|
24
|
+
columns = [
|
25
|
+
ItemColumn('stashid'),
|
26
|
+
ItemColumn('branched_from'),
|
27
|
+
ItemColumn('sha1'),
|
28
|
+
ItemColumn('msg'),
|
29
|
+
ItemColumn('line', width=0),
|
30
|
+
]
|
31
|
+
|
32
|
+
def iterload(self):
|
33
|
+
for line in self.git_lines('stash', *self.gitargs):
|
34
|
+
stashid, ctx, rest = line.split(': ', 2)
|
35
|
+
if ctx.startswith('WIP on '):
|
36
|
+
branched_from = ctx[len('WIP on '):]
|
37
|
+
sha1, msg = rest.split(' ', 1)
|
38
|
+
elif ctx.startswith('On '):
|
39
|
+
branched_from = ctx[len('On '):]
|
40
|
+
sha1 = ''
|
41
|
+
msg = rest
|
42
|
+
yield AttrDict(
|
43
|
+
line=line,
|
44
|
+
stashid=stashid,
|
45
|
+
branched_from=branched_from,
|
46
|
+
sha1=sha1,
|
47
|
+
msg=msg.strip(),
|
48
|
+
)
|
49
|
+
|
50
|
+
def openRow(self, row):
|
51
|
+
'open this stashed change'
|
52
|
+
return GitDiffSheet(row.stashid, "diffs", gitargs=['stash show --no-color --patch', row.stashid], source=self.source)
|
53
|
+
|
54
|
+
|
55
|
+
GitSheet.addCommand('', 'git-open-stashes', 'vd.push(git_stash(source, ["list"]))', 'push stashes sheet')
|
56
|
+
|
57
|
+
GitStashes.addCommand('a', 'git-stash-apply', 'loggit("stash", "apply", cursorRow[0])', 'apply this stashed change without removing')
|
58
|
+
GitStashes.addCommand('', 'git-stash-pop', 'loggit("stash", "pop", cursorRow[0])', 'apply this stashed change and drop it')
|
59
|
+
GitStashes.addCommand('d', 'git-stash-drop', 'loggit("stash", "drop", cursorRow[0])', 'drop this stashed change')
|
60
|
+
GitStashes.addCommand('b', 'git-stash-branch', 'loggit("stash", "branch", input("create branch from stash named: "), cursorRow[0])', 'create branch from stash')
|
61
|
+
|
62
|
+
|
63
|
+
vd.addMenuItems('''
|
64
|
+
Git > Open > stashes > git-open-stashes
|
65
|
+
Git > Stash > apply > git-stash-apply
|
66
|
+
Git > Stash > drop > git-stash-drop
|
67
|
+
Git > Stash > apply then drop > git-stash-pop
|
68
|
+
Git > Stash > create branch > git-stash-branch
|
69
|
+
''')
|
@@ -0,0 +1,204 @@
|
|
1
|
+
from visidata import vd, Column, VisiData, ItemColumn, Path, AttrDict, BaseSheet, IndexSheet
|
2
|
+
from visidata import RowColorizer, CellColorizer
|
3
|
+
from visidata import filesize, modtime, date
|
4
|
+
|
5
|
+
from .gitsheet import GitSheet
|
6
|
+
#from .diff import DifferSheet
|
7
|
+
|
8
|
+
vd.option('vgit_show_ignored', False, 'show ignored files on git status')
|
9
|
+
vd.theme_option('color_git_staged_mod', 'green', 'color of files staged with modifications')
|
10
|
+
vd.theme_option('color_git_staged_add', 'green', 'color of files staged for addition')
|
11
|
+
vd.theme_option('color_git_staged_del', 'red', 'color of files staged for deletion')
|
12
|
+
vd.theme_option('color_git_unstaged_del', 'on 88', 'color of files deleted but unstaged')
|
13
|
+
vd.theme_option('color_git_untracked', '243 blue', 'color of ignored/untracked files')
|
14
|
+
|
15
|
+
|
16
|
+
@VisiData.api
|
17
|
+
def git_status(vd, p, args, **kwargs):
|
18
|
+
vs = GitStatus('/'.join(p.parts[-2:]), source=p)
|
19
|
+
if not vs.gitRootPath:
|
20
|
+
return vd.git_repos(p, [])
|
21
|
+
return vs
|
22
|
+
|
23
|
+
class GitFile:
|
24
|
+
def __init__(self, path, gitsrc):
|
25
|
+
self.path = path
|
26
|
+
self.filename = path.relative_to(gitsrc)
|
27
|
+
self.is_dir = self.path.is_dir()
|
28
|
+
|
29
|
+
def __str__(self):
|
30
|
+
return str(self.filename) + (self.is_dir and '/' or '')
|
31
|
+
|
32
|
+
class GitStatus(GitSheet):
|
33
|
+
rowtype = 'files' # rowdef: GitFile
|
34
|
+
guide = '''
|
35
|
+
# git status
|
36
|
+
An overview of the local git checkout.
|
37
|
+
|
38
|
+
- `Enter` to open diff of file (`git diff`)
|
39
|
+
- `a` to stage changes in file (`git add`)
|
40
|
+
- `r` to unstage changes in file (`git reset`)
|
41
|
+
- `c` to revert all unstaged changes in file (`git checkout`)
|
42
|
+
- `d` to stage the entire file for deletion (`git rm`)
|
43
|
+
- `z Ctrl+S` to commit staged changes (`git commit`)
|
44
|
+
'''
|
45
|
+
|
46
|
+
columns = [
|
47
|
+
Column('path', width=40, getter=lambda c,r: str(r)),
|
48
|
+
Column('status', getter=lambda c,r: c.sheet.statusText(c.sheet.git_status(r)), width=8),
|
49
|
+
Column('status_raw', getter=lambda c,r: c.sheet.git_status(r), width=0),
|
50
|
+
Column('staged', getter=lambda c,r: c.sheet.git_status(r).dels),
|
51
|
+
Column('unstaged', getter=lambda c,r: c.sheet.git_status(r).adds),
|
52
|
+
Column('type', getter=lambda c,r: r.is_dir() and '/' or r.suffix, width=0),
|
53
|
+
Column('size', type=int, getter=lambda c,r: filesize(r)),
|
54
|
+
Column('modtime', type=date, getter=lambda c,r: modtime(r)),
|
55
|
+
]
|
56
|
+
nKeys = 1
|
57
|
+
|
58
|
+
colorizers = [
|
59
|
+
CellColorizer(3, 'color_git_staged_mod', lambda s,c,r,v: r and c and c.name == 'staged' and s.git_status(r).status[0] == 'M'), # staged mod
|
60
|
+
CellColorizer(1, 'color_git_staged_del', lambda s,c,r,v: r and c and c.name == 'staged' and s.git_status(r).status == 'D '), # staged delete
|
61
|
+
RowColorizer(1, 'color_git_staged_add', lambda s,c,r,v: r and s.git_status(r).status in ['A ', 'M ']), # staged add/mod
|
62
|
+
RowColorizer(1, 'color_git_unstaged_del', lambda s,c,r,v: r and s.git_status(r).status[1] == 'D'), # unstaged delete
|
63
|
+
RowColorizer(3, 'color_git_untracked', lambda s,c,r,v: r and s.git_status(r).status == '!!'), # ignored
|
64
|
+
RowColorizer(1, 'color_git_untracked', lambda s,c,r,v: r and s.git_status(r).status == '??'), # untracked
|
65
|
+
]
|
66
|
+
|
67
|
+
def statusText(self, st):
|
68
|
+
vmod = {'A': 'add', 'D': 'rm', 'M': 'mod', 'T': 'chmod', '?': '', '!': 'ignored', 'U': 'unmerged'}
|
69
|
+
x, y = st.status
|
70
|
+
if st == '??': # untracked
|
71
|
+
return 'new'
|
72
|
+
elif st == '!!': # ignored
|
73
|
+
return 'ignored'
|
74
|
+
elif x != ' ' and y == ' ': # staged
|
75
|
+
return vmod.get(x, x)
|
76
|
+
elif y != ' ': # unstaged
|
77
|
+
return vmod.get(y, y)
|
78
|
+
else:
|
79
|
+
return ''
|
80
|
+
|
81
|
+
@property
|
82
|
+
def workdir(self):
|
83
|
+
return str(self.source)
|
84
|
+
|
85
|
+
def git_status(self, r):
|
86
|
+
'''return tuple of (status, adds, dels).
|
87
|
+
status like !! ??
|
88
|
+
adds and dels are lists of additions and deletions.
|
89
|
+
'''
|
90
|
+
if not r:
|
91
|
+
return None
|
92
|
+
|
93
|
+
fn = str(r)
|
94
|
+
ret = self._cachedStatus.get(fn, None)
|
95
|
+
if not ret:
|
96
|
+
ret = AttrDict(status='??')
|
97
|
+
self._cachedStatus[fn] = ret
|
98
|
+
|
99
|
+
return ret
|
100
|
+
|
101
|
+
def ignored(self, fn):
|
102
|
+
if self.options.vgit_show_ignored:
|
103
|
+
return False
|
104
|
+
|
105
|
+
if fn in self._cachedStatus:
|
106
|
+
return self._cachedStatus[fn].status == '!!'
|
107
|
+
|
108
|
+
return False
|
109
|
+
|
110
|
+
@property
|
111
|
+
def remotediff(self):
|
112
|
+
return self.gitBranchStatuses.get(self.branch, 'no branch')
|
113
|
+
|
114
|
+
def iterload(self):
|
115
|
+
files = [GitFile(p, self.source) for p in self.source.iterdir() if p.base_stem not in ('.git')] # files in working dir
|
116
|
+
|
117
|
+
filenames = dict((gf.filename, gf) for gf in files)
|
118
|
+
|
119
|
+
self._cachedStatus.clear()
|
120
|
+
for fn in self.git_iter('ls-files', '-z'):
|
121
|
+
self._cachedStatus[fn] = AttrDict(status=' ')
|
122
|
+
|
123
|
+
for line in self.git_iter('status', '-z', '-unormal', '--ignored'):
|
124
|
+
if not line: continue
|
125
|
+
|
126
|
+
if line[2:3] == ' ':
|
127
|
+
st, fn = line[:2], line[3:]
|
128
|
+
else:
|
129
|
+
fn = line
|
130
|
+
st = '??' # untracked file
|
131
|
+
|
132
|
+
self._cachedStatus[fn] = AttrDict(status=st)
|
133
|
+
if not self.ignored(fn):
|
134
|
+
yield Path(fn)
|
135
|
+
|
136
|
+
for line in self.git_iter('diff-files', '--numstat', '-z'):
|
137
|
+
if not line: continue
|
138
|
+
adds, dels, fn = line.split('\t')
|
139
|
+
if fn not in self._cachedStatus:
|
140
|
+
self._cachedStatus[fn] = AttrDict(status='##')
|
141
|
+
cs = self._cachedStatus[fn]
|
142
|
+
cs.adds = '+%s/-%s' % (adds, dels)
|
143
|
+
|
144
|
+
for line in self.git_iter('diff-index', '--cached', '--numstat', '-z', 'HEAD'):
|
145
|
+
if not line: continue
|
146
|
+
adds, dels, fn = line.split('\t')
|
147
|
+
if fn not in self._cachedStatus:
|
148
|
+
self._cachedStatus[fn] = AttrDict(status='$$')
|
149
|
+
cs = self._cachedStatus[fn]
|
150
|
+
cs.dels = '+%s/-%s' % (adds, dels)
|
151
|
+
|
152
|
+
self.orderBy(None, self.columns[-1], reverse=True)
|
153
|
+
|
154
|
+
self.recalc() # erase column caches
|
155
|
+
|
156
|
+
def openRow(self, row):
|
157
|
+
'Open unstaged diffs for this file, or dive into directory'
|
158
|
+
if row.is_dir:
|
159
|
+
return GitStatus(row.path)
|
160
|
+
else:
|
161
|
+
return DifferSheet(row, "HEAD", "index", "working", source=sheet)
|
162
|
+
|
163
|
+
def openRows(self, rows):
|
164
|
+
'Open unstaged hunks for selected rows'
|
165
|
+
return getHunksSheet(sheet, *rows)
|
166
|
+
|
167
|
+
|
168
|
+
@GitStatus.lazy_property
|
169
|
+
def _cachedStatus(self):
|
170
|
+
return {} # [filename] -> AttrDict(status='xx', adds=, dels=)
|
171
|
+
|
172
|
+
|
173
|
+
GitStatus.addCommand('a', 'git-add', 'loggit("add", cursorRow.filename)', 'add this new file or modified file to staging')
|
174
|
+
#GitStatus.addCommand('m', 'git-mv', 'loggit("mv", cursorRow.filename, input("rename file to: ", value=cursorRow.filename))', 'rename this file')
|
175
|
+
GitStatus.addCommand('d', 'git-rm', 'loggit("rm", cursorRow.filename)', 'stage this file for deletion')
|
176
|
+
GitStatus.addCommand('r', 'git-reset', 'loggit("reset", "HEAD", cursorRow.filename)', 'reset/unstage this file')
|
177
|
+
GitStatus.addCommand('c', 'git-checkout', 'loggit("checkout", cursorRow.filename)', 'checkout this file')
|
178
|
+
GitStatus.addCommand('ga', 'git-add-selected', 'loggit("add", *[r for r in selectedRows])', 'add all selected files to staging')
|
179
|
+
GitStatus.addCommand('gd', 'git-rm-selected', 'loggit("rm", *[r for r in selectedRows])', 'delete all selected files')
|
180
|
+
GitStatus.addCommand(None, 'git-commit', 'loggit("commit", "-m", input("commit message: "))', 'commit changes')
|
181
|
+
GitStatus.addCommand(None, 'git-ignore-file', 'open(rootPath/".gitignore", "a").write(cursorRow.filename+"\\n"); reload()', 'add file to toplevel .gitignore')
|
182
|
+
GitStatus.addCommand(None, 'git-ignore-wildcard', 'open(rootPath/.gitignore, "a").write(input("add wildcard to .gitignore: "))', 'add input line to toplevel .gitignore')
|
183
|
+
|
184
|
+
|
185
|
+
#GitStatus.addCommand('z^J', 'diff-file-staged', 'vd.push(getStagedHunksSheet(sheet, cursorRow))', 'push staged diffs for this file')
|
186
|
+
#GitStatus.addCommand('gz^J', 'diff-selected-staged', 'vd.push(getStagedHunksSheet(sheet, *(selectedRows or rows)))', 'push staged diffs for selected files or all files')
|
187
|
+
#GitStatus.addCommand('^O', 'sysopen-row', 'launchExternalEditorPath(Path(cursorRow.path))', 'open this file in $EDITOR')
|
188
|
+
|
189
|
+
|
190
|
+
vd.addMenuItems('''
|
191
|
+
Git > View staged changes > current file > diff-file-staged
|
192
|
+
Git > View staged changes > selected files > staged changes > diff-selected-staged
|
193
|
+
Git > Stage > current file > git-add
|
194
|
+
Git > Stage > selected files > git-add-selected
|
195
|
+
Git > Unstage > current file > git-reset
|
196
|
+
Git > Unstage > selected files > git-reset-selected
|
197
|
+
Git > Rename file > git-mv
|
198
|
+
Git > Delete > file > git-rm
|
199
|
+
Git > Delete > selected files > git-rm-selected
|
200
|
+
Git > Ignore > file > ignore-file
|
201
|
+
Git > Ignore > wildcard > ignore-wildcard
|
202
|
+
Git > Commit staged changes > git-commit
|
203
|
+
Git > Revert unstaged changes > current file > git-checkout
|
204
|
+
''')
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from .gitsheet import vd, GitSheet
|
2
|
+
|
3
|
+
|
4
|
+
GitSheet.options.disp_status_fmt = '{sheet.progressStatus}‹{sheet.branchStatus}› {sheet.name}| '
|
5
|
+
|
6
|
+
@GitSheet.property
|
7
|
+
def progressStatus(sheet):
|
8
|
+
inp = sheet.gitInProgress()
|
9
|
+
return ('[%s] ' % inp) if inp else ''
|
10
|
+
|
11
|
+
|
12
|
+
@GitSheet.property
|
13
|
+
def branchStatus(sheet):
|
14
|
+
if hasattr(sheet.gitRootSheet, 'branch'):
|
15
|
+
return '%s%s' % (sheet.rootSheet.branch, sheet.rootSheet.remotediff)
|
16
|
+
return ''
|
17
|
+
|
18
|
+
|
19
|
+
@GitSheet.api
|
20
|
+
def gitInProgress(sheet):
|
21
|
+
p = sheet.gitPath
|
22
|
+
if not p:
|
23
|
+
return 'no repo'
|
24
|
+
if (p/'rebase-merge').exists() or (p/'rebase-apply/rebasing').exists():
|
25
|
+
return 'rebasing'
|
26
|
+
elif p/'rebase-apply'.exists():
|
27
|
+
return 'applying'
|
28
|
+
elif p/'CHERRY_PICK_HEAD'.exists():
|
29
|
+
return 'cherry-picking'
|
30
|
+
elif p/'MERGE_HEAD'.exists():
|
31
|
+
return 'merging'
|
32
|
+
elif p/'BISECT_LOG'.exists():
|
33
|
+
return 'bisecting'
|
34
|
+
return ''
|
visidata/basesheet.py
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
import visidata
|
4
|
-
from visidata import Extensible, VisiData, vd, EscapeException,
|
5
|
-
from unittest import mock
|
4
|
+
from visidata import Extensible, VisiData, vd, EscapeException, MissingAttrFormatter, AttrDict
|
6
5
|
|
7
6
|
|
8
7
|
UNLOADED = tuple() # sentinel for a sheet not yet loaded for the first time
|
@@ -98,6 +97,7 @@ class BaseSheet(DrawablePane):
|
|
98
97
|
rowtype = 'objects' # one word, plural, describing the items
|
99
98
|
precious = True # False for a few discardable metasheets
|
100
99
|
defer = False # False for not deferring changes until save
|
100
|
+
guide = '' # default to show in sidebar
|
101
101
|
|
102
102
|
def _obj_options(self):
|
103
103
|
return vd.OptionsObject(vd._options, obj=self)
|
@@ -107,15 +107,14 @@ class BaseSheet(DrawablePane):
|
|
107
107
|
|
108
108
|
class_options = options = _dualproperty(_obj_options, _class_options)
|
109
109
|
|
110
|
-
def __init__(self, *names, **kwargs):
|
110
|
+
def __init__(self, *names, rows=UNLOADED, **kwargs):
|
111
111
|
self._name = None # initial cache value necessary for self.options
|
112
|
-
self.
|
112
|
+
self.loading = False
|
113
|
+
self.names = list(names)
|
113
114
|
self.name = self.options.name_joiner.join(str(x) for x in self.names if x)
|
114
115
|
self.source = None
|
115
|
-
self.rows =
|
116
|
-
self._scr =
|
117
|
-
self.mouseX = 0
|
118
|
-
self.mouseY = 0
|
116
|
+
self.rows = rows # list of opaque objects
|
117
|
+
self._scr = None
|
119
118
|
self.hasBeenModified = False
|
120
119
|
|
121
120
|
super().__init__(**kwargs)
|
@@ -153,6 +152,14 @@ class BaseSheet(DrawablePane):
|
|
153
152
|
def __str__(self):
|
154
153
|
return self.name
|
155
154
|
|
155
|
+
@property
|
156
|
+
def rows(self):
|
157
|
+
return self._rows
|
158
|
+
|
159
|
+
@rows.setter
|
160
|
+
def rows(self, rows):
|
161
|
+
self._rows = rows
|
162
|
+
|
156
163
|
@property
|
157
164
|
def nRows(self):
|
158
165
|
'Number of rows on this sheet. Override in subclass.'
|
@@ -165,11 +172,26 @@ class BaseSheet(DrawablePane):
|
|
165
172
|
return vs in self.source
|
166
173
|
return False
|
167
174
|
|
168
|
-
|
169
|
-
|
175
|
+
@property
|
176
|
+
def displaySource(self):
|
177
|
+
if isinstance(self.source, BaseSheet):
|
178
|
+
return f'the *{self.source[0]}* sheet'
|
179
|
+
|
180
|
+
if isinstance(self.source, (list, tuple)):
|
181
|
+
if len(self.source) == 1:
|
182
|
+
return f'the **{self.source[0]}** sheet'
|
183
|
+
return f'{len(self.source)} sheets'
|
184
|
+
|
185
|
+
return f'**{self.source}**'
|
186
|
+
|
187
|
+
def execCommand(self, longname, vdglobals=None, keystrokes=None):
|
188
|
+
if ' ' in longname:
|
189
|
+
cmd, arg = longname.split(' ', maxsplit=1)
|
190
|
+
vd.injectInput(arg)
|
191
|
+
|
192
|
+
cmd = self.getCommand(longname or keystrokes)
|
170
193
|
if not cmd:
|
171
|
-
|
172
|
-
vd.status('no command for %s' % keystrokes)
|
194
|
+
vd.warning('no command for %s' % (longname or keystrokes))
|
173
195
|
return False
|
174
196
|
|
175
197
|
escaped = False
|
@@ -183,25 +205,30 @@ class BaseSheet(DrawablePane):
|
|
183
205
|
try:
|
184
206
|
for hookfunc in vd.beforeExecHooks:
|
185
207
|
hookfunc(self, cmd, '', keystrokes)
|
186
|
-
vd.debug(cmd.longname)
|
187
208
|
escaped = super().execCommand2(cmd, vdglobals=vdglobals)
|
188
209
|
except Exception as e:
|
189
210
|
vd.debug(cmd.execstr)
|
190
211
|
err = vd.exceptionCaught(e)
|
191
212
|
escaped = True
|
192
213
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
vd.cmdlog.afterExecSheet(vd.activeSheet, escaped, err)
|
197
|
-
except Exception as e:
|
198
|
-
vd.exceptionCaught(e)
|
214
|
+
if vd.cmdlog:
|
215
|
+
# sheet may have changed
|
216
|
+
vd.callNoExceptions(vd.cmdlog.afterExecSheet, vd.activeSheet, escaped, err)
|
199
217
|
|
200
|
-
self.
|
218
|
+
vd.callNoExceptions(self.checkCursor)
|
201
219
|
|
202
220
|
vd.clearCaches()
|
221
|
+
|
222
|
+
for t in self.currentThreads:
|
223
|
+
if not hasattr(t, 'lastCommand'):
|
224
|
+
t.lastCommand = True
|
225
|
+
|
203
226
|
return escaped
|
204
227
|
|
228
|
+
@property
|
229
|
+
def lastCommandThreads(self):
|
230
|
+
return [t for t in self.currentThreads if getattr(t, 'lastCommand', None)]
|
231
|
+
|
205
232
|
@property
|
206
233
|
def names(self):
|
207
234
|
return self._names
|
@@ -224,8 +251,7 @@ class BaseSheet(DrawablePane):
|
|
224
251
|
self._name = self.maybeClean(str(name))
|
225
252
|
|
226
253
|
def maybeClean(self, s):
|
227
|
-
|
228
|
-
s = cleanName(s)
|
254
|
+
'stub'
|
229
255
|
return s
|
230
256
|
|
231
257
|
def recalc(self):
|
@@ -233,10 +259,8 @@ class BaseSheet(DrawablePane):
|
|
233
259
|
pass
|
234
260
|
|
235
261
|
def refresh(self):
|
236
|
-
'
|
237
|
-
|
238
|
-
self._scr.clear()
|
239
|
-
self._scr.refresh()
|
262
|
+
'Recalculate any internal state needed for `draw()`. Overridable.'
|
263
|
+
pass
|
240
264
|
|
241
265
|
def ensureLoaded(self):
|
242
266
|
'Call ``reload()`` if not already loaded.'
|
@@ -257,30 +281,14 @@ class BaseSheet(DrawablePane):
|
|
257
281
|
'Check cursor and fix if out-of-bounds. Overridable.'
|
258
282
|
pass
|
259
283
|
|
260
|
-
def checkCursorNoExceptions(self):
|
261
|
-
try:
|
262
|
-
return self.checkCursor()
|
263
|
-
except Exception as e:
|
264
|
-
vd.exceptionCaught(e)
|
265
|
-
|
266
284
|
def evalExpr(self, expr, **kwargs):
|
267
285
|
'Evaluate Python expression *expr* in the context of *kwargs* (may vary by sheet type).'
|
268
286
|
return eval(expr, vd.getGlobals(), None)
|
269
287
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
return self._sidebar
|
288
|
+
def formatString(self, fmt, **kwargs):
|
289
|
+
'Return formatted string with *sheet* and *vd* accessible to expressions. Missing expressions return empty strings instead of error.'
|
290
|
+
return MissingAttrFormatter().format(fmt, sheet=self, vd=vd, **kwargs)
|
274
291
|
|
275
|
-
@sidebar.setter
|
276
|
-
def sidebar(self, v):
|
277
|
-
'Default implementation just sets value. Overridable.'
|
278
|
-
self._sidebar = v
|
279
|
-
|
280
|
-
@property
|
281
|
-
def sidebar_title(self):
|
282
|
-
'Default implementation returns fixed value. Overridable.'
|
283
|
-
return 'sidebar'
|
284
292
|
|
285
293
|
|
286
294
|
@VisiData.api
|
@@ -288,10 +296,11 @@ def redraw(vd):
|
|
288
296
|
'Clear the terminal screen and let the next draw cycle recreate the windows and redraw everything.'
|
289
297
|
for vs in vd.sheets:
|
290
298
|
vs._scr = None
|
291
|
-
vd.
|
292
|
-
vd.
|
293
|
-
vd.
|
294
|
-
|
299
|
+
if vd.win1: vd.win1.clear()
|
300
|
+
if vd.win2: vd.win2.clear()
|
301
|
+
if vd.scrFull:
|
302
|
+
vd.scrFull.clear()
|
303
|
+
vd.setWindows(vd.scrFull)
|
295
304
|
|
296
305
|
|
297
306
|
@VisiData.property
|
@@ -299,7 +308,7 @@ def sheet(self):
|
|
299
308
|
return self.activeSheet
|
300
309
|
|
301
310
|
@VisiData.api
|
302
|
-
def isLongname(self, ks):
|
311
|
+
def isLongname(self, ks:str):
|
303
312
|
'Return True if *ks* is a longname.'
|
304
313
|
return ('-' in ks) and (ks[-1] != '-') or (len(ks) > 3 and ks.islower())
|
305
314
|
|