visidata 2.11.1__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 +259 -42
- visidata/_open.py +84 -29
- visidata/_types.py +21 -3
- 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
- {vgit → visidata/apps/vgit}/blame.py +5 -2
- {vgit → visidata/apps/vgit}/branch.py +31 -16
- {vgit → visidata/apps/vgit}/config.py +3 -3
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- {vgit → visidata/apps/vgit}/grep.py +6 -5
- visidata/apps/vgit/log.py +81 -0
- {vgit → visidata/apps/vgit}/main.py +18 -5
- {vgit → visidata/apps/vgit}/remote.py +8 -4
- visidata/apps/vgit/repos.py +71 -0
- {vgit → visidata/apps/vgit}/setup.py +6 -4
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- {vgit → visidata/apps/vgit}/statusbar.py +2 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +208 -93
- visidata/choose.py +6 -6
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +73 -17
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +88 -114
- visidata/color.py +142 -56
- visidata/column.py +121 -129
- 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 +30 -5
- 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} +3 -1
- 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} +32 -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 +2 -4
- 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/macros.py +129 -42
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -155
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +301 -148
- visidata/man/vd.txt +290 -153
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +78 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +113 -32
- visidata/pivot.py +73 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +37 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -25
- 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 +17 -5
- 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.1.data → visidata-3.0.data}/data/share/man/man1/vd.1 +301 -148
- {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +301 -148
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/METADATA +12 -8
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- vgit/__init__.py +0 -1
- vgit/gitsheet.py +0 -164
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.1.data/scripts/vgit +0 -9
- visidata-2.11.1.dist-info/RECORD +0 -155
- {vgit → visidata/apps/vgit}/__main__.py +0 -0
- {vgit → visidata/apps/vgit}/abort.py +0 -0
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.1.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
'''
|
2
|
+
Specify the billing_project_id as the netloc, and the actual dataset_id as the path:
|
3
|
+
|
4
|
+
vdsql bigquery://<billing_project>/<dataset_id>''
|
5
|
+
'''
|
6
|
+
|
7
|
+
|
8
|
+
from visidata import vd, VisiData, Sheet, AttrColumn
|
9
|
+
from . import IbisTableSheet, IbisTableIndexSheet, IbisConnectionPool
|
10
|
+
|
11
|
+
import ibis
|
12
|
+
import ibis.expr.operations as ops
|
13
|
+
|
14
|
+
@VisiData.api
|
15
|
+
def openurl_bigquery(vd, p, filetype=None):
|
16
|
+
vd.configure_ibis()
|
17
|
+
vd.configure_bigquery()
|
18
|
+
return BigqueryDatabaseIndexSheet(p.base_stem, source=p, ibis_con=None)
|
19
|
+
|
20
|
+
vd.openurl_bq = vd.openurl_bigquery
|
21
|
+
|
22
|
+
|
23
|
+
@VisiData.api
|
24
|
+
def configure_bigquery(vd):
|
25
|
+
@ibis.bigquery.add_operation(ops.TimestampDiff)
|
26
|
+
def bq_timestamp_diff(t, expr):
|
27
|
+
op = expr.op()
|
28
|
+
left = t.translate(op.left)
|
29
|
+
right = t.translate(op.right)
|
30
|
+
return f"TIMESTAMP_DIFF({left}, {right}, SECOND)"
|
31
|
+
|
32
|
+
|
33
|
+
class BigqueryDatabaseIndexSheet(Sheet):
|
34
|
+
rowtype = 'databases' # rowdef: DatasetListItem
|
35
|
+
columns = [
|
36
|
+
# AttrColumn('project', width=0),
|
37
|
+
AttrColumn('dataset_id'),
|
38
|
+
AttrColumn('friendly_name'),
|
39
|
+
AttrColumn('full_dataset_id', width=0),
|
40
|
+
AttrColumn('labels'),
|
41
|
+
]
|
42
|
+
nKeys = 1
|
43
|
+
|
44
|
+
@property
|
45
|
+
def con(self):
|
46
|
+
if not self.ibis_con:
|
47
|
+
import ibis
|
48
|
+
self.ibis_con = ibis.connect(self.source)
|
49
|
+
return self.ibis_con
|
50
|
+
|
51
|
+
def iterload(self):
|
52
|
+
yield from self.con.client.list_datasets(project=self.source.name)
|
53
|
+
|
54
|
+
def openRow(self, row):
|
55
|
+
return IbisTableIndexSheet(row.dataset_id,
|
56
|
+
database_name=self.source.name+'.'+row.dataset_id,
|
57
|
+
ibis_con=self.con,
|
58
|
+
ibis_conpool=IbisConnectionPool(f"{self.source}/{row.dataset_id}"),
|
59
|
+
source=row,
|
60
|
+
filetype=None,
|
61
|
+
sheet_type=IbisTableSheet)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from visidata import BaseException, vd, VisiData, Progress
|
4
|
+
|
5
|
+
from ._ibis import IbisTableSheet, IbisTableIndexSheet, IbisConnectionPool
|
6
|
+
|
7
|
+
|
8
|
+
@VisiData.api
|
9
|
+
def openurl_clickhouse(vd, p, filetype=None):
|
10
|
+
vd.configure_ibis()
|
11
|
+
return IbisTableIndexSheet(p.base_stem, source=p, filetype=None, database_name=None,
|
12
|
+
ibis_conpool=IbisConnectionPool(p), sheet_type=ClickhouseSheet)
|
13
|
+
|
14
|
+
vd.openurl_clickhouses = vd.openurl_clickhouse
|
15
|
+
|
16
|
+
|
17
|
+
class ClickhouseSheet(IbisTableSheet):
|
18
|
+
@property
|
19
|
+
def countRows(self):
|
20
|
+
if self.total_rows is not None:
|
21
|
+
return self.total_rows
|
22
|
+
return super().countRows
|
23
|
+
|
24
|
+
def iterload(self):
|
25
|
+
with self.con as con:
|
26
|
+
qid = None
|
27
|
+
try:
|
28
|
+
if self.query is None:
|
29
|
+
self.query = self.baseQuery(con)
|
30
|
+
|
31
|
+
self.reloadColumns(self.query, start=0) # columns based on query without metadata
|
32
|
+
sqlstr = con.compile(self.query.limit(self.options.ibis_limit or None))
|
33
|
+
|
34
|
+
with Progress(gerund='clickhousing', sheet=self) as prog:
|
35
|
+
settings = {'max_block_size': 10000}
|
36
|
+
with con.con.query_rows_stream(sqlstr, settings) as stream:
|
37
|
+
prog.total = int(stream.source.summary['total_rows_to_read'])
|
38
|
+
prog.made = 0
|
39
|
+
for row in stream:
|
40
|
+
prog.made += 1
|
41
|
+
yield row
|
42
|
+
self.total_rows = prog.total
|
43
|
+
|
44
|
+
except Exception as e:
|
45
|
+
raise
|
46
|
+
except BaseException:
|
47
|
+
if qid:
|
48
|
+
con.con.cancel(qid)
|
49
|
+
|
50
|
+
|
51
|
+
ClickhouseSheet.init('total_rows', lambda: None)
|
52
|
+
|
53
|
+
#ClickhouseSheet.class_options.sql_always_count = True
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
|
3
|
+
from setuptools import setup, find_packages
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
|
7
|
+
exec(Path('__about__.py').read_text())
|
8
|
+
|
9
|
+
|
10
|
+
def readme():
|
11
|
+
return Path("README.md").read_text()
|
12
|
+
|
13
|
+
|
14
|
+
def requirements():
|
15
|
+
return Path("requirements.txt").read_text().splitlines()
|
16
|
+
|
17
|
+
def requirements_extra():
|
18
|
+
return Path("requirements-extra.txt").read_text().splitlines()
|
19
|
+
|
20
|
+
|
21
|
+
setup(
|
22
|
+
name="vdsql",
|
23
|
+
version=__version__,
|
24
|
+
description=__description__,
|
25
|
+
long_description=readme(),
|
26
|
+
long_description_content_type="text/markdown",
|
27
|
+
classifiers=[
|
28
|
+
"Development Status :: 4 - Beta",
|
29
|
+
"Programming Language :: Python :: 3",
|
30
|
+
],
|
31
|
+
keywords="visidata sql rdbms ibis substrait",
|
32
|
+
author="Saul Pwanson",
|
33
|
+
url="https://github.com/visidata/vdsql",
|
34
|
+
python_requires=">=3.9",
|
35
|
+
packages=find_packages(exclude=["tests"]),
|
36
|
+
entry_points={'visidata.plugins': 'vdsql=visidata.apps.vdsql'},
|
37
|
+
scripts=['vdsql'],
|
38
|
+
install_requires=requirements(),
|
39
|
+
extra_requires=requirements_extra(),
|
40
|
+
)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from visidata import vd, BaseException, VisiData
|
4
|
+
|
5
|
+
from ._ibis import IbisTableSheet, IbisConnectionPool, IbisTableIndexSheet
|
6
|
+
|
7
|
+
|
8
|
+
@VisiData.api
|
9
|
+
def openurl_snowflake(vd, p, filetype=None):
|
10
|
+
return IbisTableIndexSheet(p.base_stem, source=p, filetype=None, database_name=None,
|
11
|
+
ibis_conpool=IbisConnectionPool(p),
|
12
|
+
sheet_type=SnowflakeSheet)
|
13
|
+
|
14
|
+
|
15
|
+
class SnowflakeSheet(IbisTableSheet):
|
16
|
+
@property
|
17
|
+
def countRows(self):
|
18
|
+
r = super().countRows
|
19
|
+
if r is None and self.cursor is None:
|
20
|
+
return None # no cursor yet
|
21
|
+
return r
|
22
|
+
|
23
|
+
def executeSql(self, sql):
|
24
|
+
assert self.cursor is None
|
25
|
+
|
26
|
+
with self.con as con:
|
27
|
+
con = con.con
|
28
|
+
|
29
|
+
if self.warehouse:
|
30
|
+
con.execute(f'USE WAREHOUSE {self.warehouse}')
|
31
|
+
|
32
|
+
with con.begin() as c:
|
33
|
+
snowflake_conn = c.connection.dbapi_connection
|
34
|
+
cursor = self.cursor = snowflake_conn.cursor()
|
35
|
+
cursor.execute_async(sql)
|
36
|
+
while snowflake_conn.is_still_running(snowflake_conn.get_query_status(cursor.sfqid)):
|
37
|
+
time.sleep(.1)
|
38
|
+
|
39
|
+
cursor.get_results_from_sfqid(cursor.sfqid)
|
40
|
+
yield from cursor.fetchall()
|
41
|
+
|
42
|
+
self.cursor = None
|
43
|
+
|
44
|
+
def iterload(self):
|
45
|
+
try:
|
46
|
+
with self.con as con:
|
47
|
+
if self.query is None:
|
48
|
+
self.query = self.baseQuery(con)
|
49
|
+
yield from self.executeSql(self.ibis_to_sql(self.withRowcount(self.baseQuery(con))))
|
50
|
+
except BaseException:
|
51
|
+
if self.cursor:
|
52
|
+
self.cancelQuery(self.cursor.sfqid)
|
53
|
+
raise
|
54
|
+
|
55
|
+
self.reloadColumns(self.query) # columns based on query without metadata
|
56
|
+
|
57
|
+
def cancelQuery(self, qid):
|
58
|
+
vd.status(f'canceling "{qid}"')
|
59
|
+
with self.con as con:
|
60
|
+
with con.begin() as con:
|
61
|
+
cursor = con.connection.dbapi_connection.cursor()
|
62
|
+
cursor.execute(f"SELECT SYSTEM$CANCEL_QUERY('{qid}')")
|
63
|
+
vd.status(cursor.fetchall())
|
64
|
+
|
65
|
+
|
66
|
+
SnowflakeSheet.init('cursor', lambda: None)
|
67
|
+
SnowflakeSheet.init('warehouse', str)
|
@@ -4,7 +4,7 @@ from .gitsheet import GitSheet
|
|
4
4
|
|
5
5
|
|
6
6
|
@VisiData.api
|
7
|
-
def git_blame(vd, args, **kwargs):
|
7
|
+
def git_blame(vd, gitpath, args, **kwargs):
|
8
8
|
if args and not args[-1].startswith('-'):
|
9
9
|
fn = args[-1]
|
10
10
|
return GitBlame('blame', fn, source=Path(fn), **kwargs)
|
@@ -20,6 +20,9 @@ class FormatColumn(Column):
|
|
20
20
|
# source = GitSheet; .gitfile=GitFile
|
21
21
|
class GitBlame(GitSheet):
|
22
22
|
rowtype = 'lines'
|
23
|
+
guide = '''
|
24
|
+
# git blame
|
25
|
+
'''
|
23
26
|
columns = [
|
24
27
|
ItemColumn('sha', width=0),
|
25
28
|
ItemColumn('orig_linenum', width=0, type=int),
|
@@ -70,4 +73,4 @@ class GitBlame(GitSheet):
|
|
70
73
|
|
71
74
|
#GitBlame.addCommand(ENTER, 'diff-line', 'openDiff(str(gitfile), cursorRow[0]["sha"]+"^", cursorRow[0]["sha"])', 'open diff of the commit when this line changed')
|
72
75
|
|
73
|
-
#GitStatus.addCommand(None, 'git-blame', 'vd.push(GitBlame(cursorRow, source=sheet))', 'push blame for this file')
|
76
|
+
#GitStatus.addCommand(None, 'git-blame', 'vd.push(GitBlame(cursorRow, source=sheet))', 'push blame for this file')
|
@@ -4,17 +4,17 @@ from visidata import vd, Column, VisiData, ItemColumn, AttrColumn, Path, AttrDic
|
|
4
4
|
|
5
5
|
from .gitsheet import GitSheet
|
6
6
|
|
7
|
-
vd.
|
8
|
-
vd.
|
7
|
+
vd.theme_option('color_git_current_branch', 'underline', 'color of current branch on branches sheet')
|
8
|
+
vd.theme_option('color_git_remote_branch', 'cyan', 'color of remote branches on branches sheet')
|
9
9
|
|
10
10
|
|
11
11
|
@VisiData.api
|
12
|
-
def git_branch(vd, args):
|
12
|
+
def git_branch(vd, p, args):
|
13
13
|
nonListArgs = '--track --no-track --set-upstream-to -u --unset-upstream -m -M -c -C -d -D --edit-description'.split()
|
14
14
|
if any(x in args for x in nonListArgs):
|
15
15
|
return
|
16
16
|
|
17
|
-
return GitBranch('git-branch-list', source=
|
17
|
+
return GitBranch('git-branch-list', source=p, git_args=args)
|
18
18
|
|
19
19
|
|
20
20
|
def _remove_prefix(text, prefix):
|
@@ -32,7 +32,7 @@ class GitBranchNameColumn(Column):
|
|
32
32
|
|
33
33
|
|
34
34
|
class GitBranch(GitSheet):
|
35
|
-
|
35
|
+
guide = '''
|
36
36
|
# git branch
|
37
37
|
List of all branches, including relevant metadata.
|
38
38
|
|
@@ -61,16 +61,25 @@ class GitBranch(GitSheet):
|
|
61
61
|
nKeys = 1
|
62
62
|
|
63
63
|
def iterload(self):
|
64
|
-
branches_lines = self.git_lines(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
64
|
+
branches_lines = self.git_lines(
|
65
|
+
'branch',
|
66
|
+
'--list',
|
67
|
+
'--format',
|
68
|
+
' '.join((
|
69
|
+
'%(if)%(symref)%(then)yes%(else)no%(end)',
|
70
|
+
'%(HEAD) %(refname:short) %(objectname:short)',
|
71
|
+
'%(if)%(upstream)%(then)[%(upstream:short)',
|
72
|
+
'%(if)%(upstream:track)%(then): %(upstream:track,nobracket)%(end)]',
|
73
|
+
'%(end)',
|
74
|
+
'%(contents:subject)'
|
75
|
+
)),
|
76
|
+
'-vv',
|
77
|
+
'--no-color',
|
78
|
+
*self.git_args)
|
72
79
|
|
73
|
-
|
80
|
+
for line in branches_lines:
|
81
|
+
m = re.match(r'''(?P<is_symref>(yes|no)?)\s+
|
82
|
+
(?P<current>\*?)\s+
|
74
83
|
(?P<localbranch>\S+)\s+
|
75
84
|
(?P<refid>\w+)\s+
|
76
85
|
(?:\[
|
@@ -78,8 +87,12 @@ class GitBranch(GitSheet):
|
|
78
87
|
\s*(?P<extra>.*?)
|
79
88
|
\])?
|
80
89
|
\s*(?P<msg>.*)''', line, re.VERBOSE)
|
81
|
-
if m:
|
82
|
-
|
90
|
+
if not m:
|
91
|
+
continue
|
92
|
+
branch_details = AttrDict(m.groupdict())
|
93
|
+
if branch_details.is_symref == 'yes':
|
94
|
+
continue
|
95
|
+
yield branch_details
|
83
96
|
|
84
97
|
branch_stats = self.gitRootSheet.gitBranchStatuses
|
85
98
|
for row in Progress(self.rows):
|
@@ -126,6 +139,7 @@ def gitBranchStatuses(sheet):
|
|
126
139
|
return ret
|
127
140
|
|
128
141
|
|
142
|
+
GitSheet.addCommand('', 'git-open-branches', 'vd.push(git_branch(source, []))', 'push branches sheet')
|
129
143
|
GitSheet.addCommand('', 'git-branch-create', 'git("branch", input("create branch: ", type="branch"))', 'create a new branch off the current checkout')
|
130
144
|
GitBranch.addCommand('', 'git-branch-checkout', 'git("checkout", cursorRow.localbranch)', 'checkout this branch')
|
131
145
|
|
@@ -135,4 +149,5 @@ vd.addMenuItems('''
|
|
135
149
|
Git > Branch > delete > git-branch-delete
|
136
150
|
Git > Branch > rename > git-branch-rename
|
137
151
|
Git > Branch > checkout > git-branch-checkout
|
152
|
+
Git > Open > branches > git-open-branches
|
138
153
|
''')
|
@@ -8,9 +8,9 @@ from .gitsheet import GitSheet
|
|
8
8
|
CONFIG_CONTEXTS = ('local', 'global', 'system')
|
9
9
|
|
10
10
|
@VisiData.api
|
11
|
-
def git_config(vd, args):
|
11
|
+
def git_config(vd, p, args):
|
12
12
|
if not args or '-l' in args:
|
13
|
-
return GitConfig('git-config', source=
|
13
|
+
return GitConfig('git-config', source=p)
|
14
14
|
|
15
15
|
vd.git_options = vd.git_config
|
16
16
|
|
@@ -37,7 +37,7 @@ class GitConfigColumn(Column):
|
|
37
37
|
|
38
38
|
|
39
39
|
class GitConfig(GitSheet):
|
40
|
-
|
40
|
+
guide = '''
|
41
41
|
# git config
|
42
42
|
Add, edit, or delete git config options.
|
43
43
|
|
@@ -0,0 +1,169 @@
|
|
1
|
+
from visidata import vd, VisiData, ItemColumn, RowColorizer, AttrDict, Column
|
2
|
+
|
3
|
+
from .gitsheet import GitSheet
|
4
|
+
|
5
|
+
vd.option('git_diff_algo', 'minimal', 'algorithm to use for git diff')
|
6
|
+
vd.theme_option('color_git_hunk_add', 'green', 'color for added hunk lines')
|
7
|
+
vd.theme_option('color_git_hunk_del', 'red', 'color for deleted hunk lines')
|
8
|
+
vd.theme_option('color_git_hunk_diff', 'yellow', 'color for hunk diffs')
|
9
|
+
|
10
|
+
@VisiData.api
|
11
|
+
def git_diff(vd, p, args):
|
12
|
+
return GitDiffSheet('git-diff', source=p, gitargs=args)
|
13
|
+
|
14
|
+
def _parseStartCount(s):
|
15
|
+
sc = s.split(',')
|
16
|
+
if len(sc) == 2:
|
17
|
+
return sc
|
18
|
+
if len(sc) == 1:
|
19
|
+
return sc[0], 1
|
20
|
+
|
21
|
+
|
22
|
+
class GitDiffSheet(GitSheet):
|
23
|
+
columns = [
|
24
|
+
ItemColumn('a_fn', width=0),
|
25
|
+
ItemColumn('fn', 'b_fn', width=30, hoffset=-28),
|
26
|
+
ItemColumn('a_lineno', type=int, width=0),
|
27
|
+
ItemColumn('lineno', 'b_lineno', type=int, width=8),
|
28
|
+
Column('count', width=10, getter=lambda c,r: c.sheet.hunkCount(r)),
|
29
|
+
ItemColumn('context'),
|
30
|
+
ItemColumn('lines', type=''.join),
|
31
|
+
]
|
32
|
+
|
33
|
+
guide = '''# {sheet.cursorRow.a_fn}
|
34
|
+
{sheet.cursorLines}'''
|
35
|
+
|
36
|
+
def hunkCount(self, row):
|
37
|
+
return f'-{row.a_count}/+{row.b_count}'
|
38
|
+
|
39
|
+
@property
|
40
|
+
def cursorLines(self):
|
41
|
+
r = ''
|
42
|
+
for line in self.cursorRow.lines[2:]:
|
43
|
+
if line.startswith('-'):
|
44
|
+
line = '[:git_hunk_del]' + line + '[/]'
|
45
|
+
elif line.startswith('+'):
|
46
|
+
line = '[:git_hunk_add]' + line + '[/]'
|
47
|
+
|
48
|
+
r += line + '\n'
|
49
|
+
r = r[4:]
|
50
|
+
return r
|
51
|
+
|
52
|
+
def iterload(self):
|
53
|
+
current_hunk = None
|
54
|
+
|
55
|
+
for line in self.git_lines('diff --patch --inter-hunk-context=2 --find-renames --no-color --no-prefix', *self.gitargs):
|
56
|
+
if line.startswith('diff'):
|
57
|
+
diff_started = True
|
58
|
+
continue
|
59
|
+
if not diff_started:
|
60
|
+
continue
|
61
|
+
|
62
|
+
if line.startswith('---'):
|
63
|
+
hunk_lines = [line] # new file
|
64
|
+
leftfn = line[4:]
|
65
|
+
elif line.startswith('+++'):
|
66
|
+
hunk_lines.append(line)
|
67
|
+
rightfn = line[4:]
|
68
|
+
elif line.startswith('@@'):
|
69
|
+
hunk_lines.append(line)
|
70
|
+
_, linenums, context = line.split('@@')
|
71
|
+
leftlinenums, rightlinenums = linenums.split()
|
72
|
+
leftstart, leftcount = _parseStartCount(leftlinenums[1:])
|
73
|
+
rightstart, rightcount = _parseStartCount(rightlinenums[1:])
|
74
|
+
current_hunk = AttrDict(
|
75
|
+
a_fn=leftfn,
|
76
|
+
b_fn=rightfn,
|
77
|
+
context=context,
|
78
|
+
a_lineno=int(leftstart),
|
79
|
+
a_count=0,
|
80
|
+
b_lineno=int(rightstart),
|
81
|
+
b_count=0,
|
82
|
+
lines=hunk_lines
|
83
|
+
)
|
84
|
+
yield current_hunk
|
85
|
+
hunk_lines = hunk_lines[:2] # keep file context only
|
86
|
+
|
87
|
+
elif line[0] in ' +-':
|
88
|
+
current_hunk.lines.append(line)
|
89
|
+
if line[0] == '+':
|
90
|
+
current_hunk.a_count += 1
|
91
|
+
elif line[0] == '-':
|
92
|
+
current_hunk.b_count += 1
|
93
|
+
|
94
|
+
def openRow(self, row):
|
95
|
+
return HunkViewer(f'{row.a_fn}:{row.a_lineno}', source=self.source, hunks=[row])
|
96
|
+
|
97
|
+
|
98
|
+
class HunkViewer(GitSheet):
|
99
|
+
colorizers = [
|
100
|
+
RowColorizer(4, 'color_git_hunk_add', lambda s,c,r,v: r and r.old != r.new and r.old is None),
|
101
|
+
RowColorizer(4, 'color_git_hunk_del', lambda s,c,r,v: r and r.old != r.new and r.new is None),
|
102
|
+
RowColorizer(5, 'color_git_hunk_diff', lambda s,c,r,v: r and r.old != r.new and r.new is not None and r.old is not None),
|
103
|
+
]
|
104
|
+
columns = [
|
105
|
+
ItemColumn('1', 'old', width=40),
|
106
|
+
ItemColumn('2', 'new', width=40),
|
107
|
+
]
|
108
|
+
|
109
|
+
def draw(self, scr):
|
110
|
+
self.column('1').width=self.windowWidth//2-1
|
111
|
+
self.column('2').width=self.windowWidth//2-1
|
112
|
+
super().draw(scr)
|
113
|
+
|
114
|
+
def iterload(self):
|
115
|
+
nextDelIdx = None
|
116
|
+
for hunk in self.hunks:
|
117
|
+
for line in hunk.lines[3:]: # diff without the patch headers
|
118
|
+
typech = line[0]
|
119
|
+
line = line[1:]
|
120
|
+
if typech == '-': # deleted
|
121
|
+
yield AttrDict(hunk=hunk, type=typech, old=line)
|
122
|
+
if nextDelIdx is None:
|
123
|
+
nextDelIdx = len(self.rows)-1
|
124
|
+
elif typech == '+': # added
|
125
|
+
if nextDelIdx is not None:
|
126
|
+
if nextDelIdx < len(self.rows):
|
127
|
+
self.rows[nextDelIdx].new = line
|
128
|
+
nextDelIdx += 1
|
129
|
+
continue
|
130
|
+
|
131
|
+
yield AttrDict(hunk=hunk, type=typech, new=line)
|
132
|
+
nextDelIdx = None
|
133
|
+
elif typech == ' ': # unchanged
|
134
|
+
yield AttrDict(hunk=hunk, type=typech, old=line, new=line)
|
135
|
+
nextDelIdx = None
|
136
|
+
else:
|
137
|
+
continue # header
|
138
|
+
|
139
|
+
|
140
|
+
HunkViewer.addCommand('2', 'git-apply-hunk', 'source.git_apply(cursorRow.hunk, "--cached"); reload()', 'apply this hunk to the index and move to the next hunk')
|
141
|
+
HunkViewer.addCommand('1', 'git-remove-hunk', 'source.git_apply(cursorRow.hunk, "--reverse"); reload()', 'remove this hunk from staging')
|
142
|
+
HunkViewer.addCommand('Enter', 'git-skip-hunk', 'hunks.pop(0); reload()', 'move to the next hunk without applying this hunk')
|
143
|
+
HunkViewer.addCommand('d', 'delete-line', 'source[7].pop(cursorRow[3]); reload()', 'delete a line from the patch')
|
144
|
+
|
145
|
+
#HunksSheet.addCommand('g^J', 'git-diff-selected', 'vd.push(HunkViewer(selectedRows or rows, source=sheet))', 'view the diffs for the selected hunks (or all hunks)')
|
146
|
+
|
147
|
+
@GitDiffSheet.api
|
148
|
+
def git_apply(sheet, row, *args):
|
149
|
+
sheet.git('apply -p0 -', *args, _in='\n'.join(row.lines)+'\n')
|
150
|
+
|
151
|
+
c = sheet.hunkCount(row)
|
152
|
+
vd.status(f'applied hunk ({c})')
|
153
|
+
sheet.reload()
|
154
|
+
|
155
|
+
|
156
|
+
#DiffSheet.addCommand('[', '', 'cursorRowIndex = findDiffRow(cursorCol.refnum, cursorRowIndex, -1)', 'go to previous diff')
|
157
|
+
#DiffSheet.addCommand(']', '', 'cursorRowIndex = findDiffRow(cursorCol.refnum, cursorRowIndex, +1)', 'go to next diff')
|
158
|
+
|
159
|
+
GitDiffSheet.addCommand('a', 'git-add-hunk', 'git_apply(cursorRow, "--cached")', 'apply this hunk to the index')
|
160
|
+
|
161
|
+
vd.addMenuItems('''
|
162
|
+
Git > Stage > current hunk > git-add-hunk
|
163
|
+
Git > Stage > current hunk > git-add-hunk
|
164
|
+
''')
|
165
|
+
|
166
|
+
vd.addGlobals(
|
167
|
+
GitDiffSheet=GitDiffSheet,
|
168
|
+
HunkViewer=HunkViewer
|
169
|
+
)
|