Full Code of saulpw/visidata for AI

develop 3d1b6b96c5b9 cached
995 files
20.4 MB
1.7M tokens
2918 symbols
1 requests
Download .txt
Showing preview only (6,652K chars total). Download the full file or copy to clipboard to get everything.
Repository: saulpw/visidata
Branch: develop
Commit: 3d1b6b96c5b9
Files: 995
Total size: 20.4 MB

Directory structure:
gitextract_wcvjea1d/

├── .codespellrc
├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   ├── launch.json
│   ├── post-create.sh
│   └── settings.json
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature-request.md
│   │   ├── loader-request.md
│   │   └── question.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── main.yml
│       ├── vdsql.yml
│       └── vgit.yml
├── .gitignore
├── .gitmodules
├── .gitpod.Dockerfile
├── .gitpod.yml
├── .mailmap
├── .theia/
│   └── settings.json
├── AGENTS.md
├── CHANGELOG.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile.alpine
├── Dockerfile.darkdraw.alpine
├── LICENSE.gpl3
├── MANIFEST.in
├── Makefile
├── README.md
├── bin/
│   ├── filter-doc.py
│   ├── vd
│   ├── vd2to3.vdx
│   └── viewtsv.py
├── dev/
│   ├── DOCS.md
│   ├── GIT.md
│   ├── OPTIONS.md
│   ├── PERFORMANCE.md
│   ├── README.md
│   ├── STYLE.md
│   ├── TESTING.md
│   ├── build-container
│   ├── checklists/
│   │   ├── add-aggregator.md
│   │   ├── add-command.md
│   │   ├── feature.md
│   │   ├── manual-tests.md
│   │   └── release.md
│   ├── debian/
│   │   ├── changelog
│   │   ├── control
│   │   ├── copyright
│   │   ├── manpages
│   │   ├── rules
│   │   ├── source/
│   │   │   ├── format
│   │   │   └── local-options
│   │   ├── upstream/
│   │   │   └── metadata
│   │   └── watch
│   ├── design/
│   │   ├── 000-notes.md
│   │   ├── 160-longnames.md
│   │   ├── 169-settings.md
│   │   ├── 173-benchmark.md
│   │   ├── 174-keycols.md
│   │   ├── 175-design-terms.md
│   │   ├── 176-miscrules.md
│   │   ├── 181-benchmark-data.md
│   │   ├── 232-input.md
│   │   ├── 260-push.md
│   │   ├── README.md
│   │   └── path.md
│   ├── diff-test.sh
│   ├── formats.jsonl
│   ├── formats.vd
│   ├── hooks/
│   │   └── pre-push
│   ├── mkman.sh
│   ├── mkpandas-df.py
│   ├── quit.vdx
│   ├── requirements-dev.txt
│   ├── run-tests-individually.sh
│   ├── test-all.sh
│   ├── test-stdin-replay.vdx
│   ├── test.sh
│   ├── types.jsonl
│   ├── vduplot.vdx
│   ├── visidata-brew.rb
│   ├── workshop-outline.md
│   ├── zsh-completion.in
│   └── zsh-completion.py
├── docs/
│   ├── README.md
│   ├── api/
│   │   ├── Makefile
│   │   ├── _static/
│   │   │   ├── .gitkeep
│   │   │   └── css/
│   │   │       └── custom.css
│   │   ├── async.rst
│   │   ├── canvas.rst
│   │   ├── columns.rst
│   │   ├── commands.rst
│   │   ├── conf.py
│   │   ├── data.rst
│   │   ├── extensible.rst
│   │   ├── guides.rst
│   │   ├── index.rst
│   │   ├── interface.rst
│   │   ├── loaders.rst
│   │   ├── make.bat
│   │   ├── modify.rst
│   │   ├── modules.rst
│   │   ├── options.rst
│   │   ├── plugins.rst
│   │   ├── requirements.txt
│   │   ├── runtime.txt
│   │   ├── sheets.rst
│   │   └── style.rst
│   ├── casts/
│   │   ├── expand-cols.cast
│   │   ├── pivot-graphs.cast
│   │   ├── pivot.cast
│   │   ├── save-restore.cast
│   │   ├── split-regex.cast
│   │   └── types.cast
│   ├── colors.md
│   ├── columns.md
│   ├── contributing.md
│   ├── crud.md
│   ├── customize.md
│   ├── dirsheet.md
│   ├── edit.md
│   ├── formats.md
│   ├── freq.md
│   ├── gmail.md
│   ├── graph.md
│   ├── graphics.md
│   ├── group.md
│   ├── index.md
│   ├── internal_formats.md
│   ├── join.md
│   ├── loading.md
│   ├── macros.md
│   ├── menu.md
│   ├── mouse.md
│   ├── move.md
│   ├── navigate.md
│   ├── pipes.md
│   ├── plugins.md
│   ├── rows.md
│   ├── save-restore.md
│   ├── shell.md
│   ├── split.md
│   ├── test.md
│   ├── usage.md
│   └── viewtsv.md
├── platform/
│   ├── windows/
│   │   ├── vdwin-installer.md
│   │   ├── vdwin.py
│   │   └── vdwin.spec
│   └── www/
│       ├── Dockerfile
│       ├── Makefile
│       └── visidatarc
├── plugins/
│   ├── geocoding.py
│   ├── pcap/
│   │   ├── iana-ports.tsv
│   │   └── wireshark-oui.tsv
│   └── vtask.py
├── pyrightconfig.json
├── requirements.scm
├── requirements.txt
├── ruff.toml
├── sample_data/
│   ├── 1794685.fec
│   ├── 2m.tsv
│   ├── StatusPR.csv
│   ├── UTF-8-demo.txt
│   ├── a.tsv
│   ├── accidents.sav
│   ├── arrays.hdf5
│   ├── arrays.npz
│   ├── b.tsv
│   ├── benchmark.arrow
│   ├── benchmark.csv
│   ├── benchmark.fixed
│   ├── benchmark.jsonl
│   ├── benchmark.lsv
│   ├── benchmark.npy
│   ├── benchmark.ods
│   ├── benchmark.psv
│   ├── benchmark.xml
│   ├── benchmark.yml
│   ├── brakes.xpt
│   ├── co3.dta
│   ├── co3.stata
│   ├── color-merged-cells.xlsx
│   ├── countries
│   ├── date-error-test.xlsx
│   ├── empty-cell.xlsx
│   ├── empty-table.html
│   ├── errors.csv
│   ├── freshwater-mammals.toml
│   ├── goog.npy
│   ├── gtm.f5log
│   ├── hello.mnu
│   ├── issue1308.html
│   ├── messenger.pcap
│   ├── numeric-cols.tsv
│   ├── officials.jsonla
│   ├── pr2815.jsonl
│   ├── sample-sales-reps.xlsx
│   ├── sample.arrow
│   ├── sample.arrows
│   ├── sample.conllu
│   ├── sample.geojson
│   ├── sample.parquet
│   ├── sample.tsv
│   ├── sample.vds
│   ├── saulpw-008.xd
│   ├── sensors.h5
│   ├── shapefile/
│   │   ├── WA_State_Boundary.cpg
│   │   ├── WA_State_Boundary.dbf
│   │   ├── WA_State_Boundary.prj
│   │   ├── WA_State_Boundary.shp
│   │   ├── WA_State_Boundary.shx
│   │   └── WA_State_Boundary.xml
│   ├── smiths-json.grep
│   ├── smiths-standard.grep
│   ├── states.yml
│   ├── sunshinelist.html
│   ├── test-fixed-leadingspaces.txt
│   ├── test-unicode-display.tsv
│   ├── test.fixed
│   ├── test.jsonl
│   ├── test.msgpack
│   ├── test.msgpackz
│   └── test.usv
├── setup.py
├── snippets/
│   ├── gender.py
│   ├── geolocate.py
│   └── rank.py
├── tests/
│   ├── .visidata/
│   │   └── .gitignore
│   ├── .visidatarc
│   ├── 1410a.vdj
│   ├── 1410b.vdj
│   ├── 1410c.vdj
│   ├── add-col-dup-attrs.vdj
│   ├── addcol-iter.vdj
│   ├── addcol_window.vd
│   ├── aggregators-cols.vdj
│   ├── aggregators-errors.vd
│   ├── aggregators-set.vd
│   ├── append.vd
│   ├── avg-nulls.vd
│   ├── bulk-rename-cols.vd
│   ├── capture-col-named.vd
│   ├── column-copy.vd
│   ├── column-name-__name__.csv
│   ├── column-name-__name__.vd
│   ├── column-name-_fields.csv
│   ├── column-name-_fields.vd
│   ├── column-name-length.csv
│   ├── column-name-length.vd
│   ├── concat-origin.vdx
│   ├── curcol.vd
│   ├── data1.tsv
│   ├── data2.tsv
│   ├── data3.tsv
│   ├── date_add.vd
│   ├── describe-error.vd
│   ├── describe-through.vd
│   ├── describe.vd
│   ├── diff-join.vdj
│   ├── dup-rows-attrs.vd
│   ├── edit-fixed.vdx
│   ├── edit-joinkey-1.vd
│   ├── edit-joinkey-2.vd
│   ├── edit-joinregular-1.vd
│   ├── edit-joinregular-2.vd
│   ├── edit-type.vd
│   ├── empty-unfurl-2.vd
│   ├── empty-unfurl.vd
│   ├── error-passthru.vd
│   ├── errors-311.vd
│   ├── exp-digits.vdj
│   ├── extend.vd
│   ├── fill-nested.vdj
│   ├── fill-zero.json
│   ├── fill-zero.vdj
│   ├── fill.vdj
│   ├── format-enum-freeze.vd
│   ├── format-enum.vd
│   ├── freeze-json.vd
│   ├── freeze-sheet-attrs.vd
│   ├── freq-dive-except.vdx
│   ├── freq-error.vd
│   ├── freq-fmtstr.vd
│   ├── freq-same-int.vd
│   ├── freq-summary.vd
│   ├── freqtbl-nested-dive-selected.vdx
│   ├── freqtbl-openrow.vd
│   ├── frozen-attrs.vd
│   ├── full-join.vd
│   ├── gMelt.vd
│   ├── getitem.vd
│   ├── golden/
│   │   ├── 1410a.tsv
│   │   ├── 1410b.tsv
│   │   ├── 1410c.tsv
│   │   ├── add-col-dup-attrs.tsv
│   │   ├── addcol-iter.tsv
│   │   ├── addcol_window.tsv
│   │   ├── aggregators-cols.tsv
│   │   ├── aggregators-errors.tsv
│   │   ├── aggregators-set.tsv
│   │   ├── append.tsv
│   │   ├── avg-nulls.tsv
│   │   ├── bulk-rename-cols.tsv
│   │   ├── capture-col-named.tsv
│   │   ├── column-copy.tsv
│   │   ├── column-name-__name__.csv
│   │   ├── column-name-_fields.csv
│   │   ├── column-name-length.csv
│   │   ├── concat-origin.tsv
│   │   ├── curcol.tsv
│   │   ├── date_add.tsv
│   │   ├── describe-error.tsv
│   │   ├── describe-through.tsv
│   │   ├── describe.tsv
│   │   ├── diff-join.tsv
│   │   ├── dup-rows-attrs.tsv
│   │   ├── edit-fixed.fixed
│   │   ├── edit-joinkey-1.tsv
│   │   ├── edit-joinkey-2.tsv
│   │   ├── edit-joinregular-1.tsv
│   │   ├── edit-joinregular-2.tsv
│   │   ├── edit-type.tsv
│   │   ├── empty-unfurl-2.tsv
│   │   ├── empty-unfurl.tsv
│   │   ├── error-passthru.tsv
│   │   ├── errors-311.tsv
│   │   ├── exp-digits.tsv
│   │   ├── extend.tsv
│   │   ├── fill-nested.jsonl
│   │   ├── fill-zero.json
│   │   ├── fill.jsonl
│   │   ├── format-enum-freeze.tsv
│   │   ├── format-enum.json
│   │   ├── format-enum.tsv
│   │   ├── freeze-json.tsv
│   │   ├── freeze-sheet-attrs.tsv
│   │   ├── freq-dive-except.tsv
│   │   ├── freq-error.tsv
│   │   ├── freq-fmtstr.tsv
│   │   ├── freq-same-int.tsv
│   │   ├── freq-summary.tsv
│   │   ├── freqtbl-nested-dive-selected.tsv
│   │   ├── freqtbl-openrow.tsv
│   │   ├── frozen-attrs.tsv
│   │   ├── full-join.tsv
│   │   ├── gMelt.tsv
│   │   ├── getitem.tsv
│   │   ├── hide-uniform.tsv
│   │   ├── histogram.tsv
│   │   ├── import-python.tsv
│   │   ├── inner-join.tsv
│   │   ├── invalid_unicode_sqlite.tsv
│   │   ├── issue1308.tsv
│   │   ├── issue1346.tsv
│   │   ├── issue1377.tsv
│   │   ├── issue1377b.tsv
│   │   ├── issue2015.tsv
│   │   ├── issue2190.tsv
│   │   ├── issue2227.tsv
│   │   ├── issue2316.tsv
│   │   ├── issue2476.csv
│   │   ├── issue2524-curcol.tsv
│   │   ├── issue350.tsv
│   │   ├── issue655.tsv
│   │   ├── issue733.tsv
│   │   ├── issue964-inner-join.tsv
│   │   ├── join-cols-single-sheet.tsv
│   │   ├── join-concat.tsv
│   │   ├── join-different-types.tsv
│   │   ├── join-extend-mult.tsv
│   │   ├── join-merge.tsv
│   │   ├── join-non-unique-cols.tsv
│   │   ├── listofdictobj.tsv
│   │   ├── load-2d-matrix.tsv
│   │   ├── load-conllu.jsonl
│   │   ├── load-csv.tsv
│   │   ├── load-dir.tsv
│   │   ├── load-fec.tsv
│   │   ├── load-fixed-header0.tsv
│   │   ├── load-fixed-leadingspaces.tsv
│   │   ├── load-fixed.tsv
│   │   ├── load-grep-json.grep
│   │   ├── load-grep-standard.grep
│   │   ├── load-h5.tsv
│   │   ├── load-html.tsv
│   │   ├── load-http-flaky.tsv
│   │   ├── load-json.tsv
│   │   ├── load-jsonla.tsv
│   │   ├── load-lsv.lsv
│   │   ├── load-lsv.tsv
│   │   ├── load-msgpack.tsv
│   │   ├── load-msgpackz.tsv
│   │   ├── load-numpy.tsv
│   │   ├── load-ods.tsv
│   │   ├── load-pandas-2.csv
│   │   ├── load-pandas-3.tsv
│   │   ├── load-pandas.tsv
│   │   ├── load-parquet.tsv
│   │   ├── load-png.tsv
│   │   ├── load-sqlite-view.tsv
│   │   ├── load-sqlite.tsv
│   │   ├── load-stata.tsv
│   │   ├── load-toml.tsv
│   │   ├── load-usv.tsv
│   │   ├── load-xlsx.tsv
│   │   ├── load-xml.tsv
│   │   ├── load-xpt-n311.tsv
│   │   ├── load-yaml.tsv
│   │   ├── load-zip.tsv
│   │   ├── load_npy.tsv
│   │   ├── load_xlsx.tsv
│   │   ├── melt-error.tsv
│   │   ├── messenger-nosave.dot
│   │   ├── monthly-revenue.tsv
│   │   ├── numeric-cols.tsv
│   │   ├── numeric-names.tsv
│   │   ├── numeric_binning.tsv
│   │   ├── outer-join-1.tsv
│   │   ├── outer-join-2.tsv
│   │   ├── pandas_loader_dup_selected.tsv
│   │   ├── petsdiet.tsv
│   │   ├── pivot-error.tsv
│   │   ├── pivot-noaggr.tsv
│   │   ├── pivot.tsv
│   │   ├── pr2302.tsv
│   │   ├── pr2308.json
│   │   ├── pr2372.tsv
│   │   ├── pr2400.tsv
│   │   ├── pr2614.tsv
│   │   ├── pr2647.tsv
│   │   ├── pr2688a.tsv
│   │   ├── pr2688b.tsv
│   │   ├── pr2815.json
│   │   ├── pr2855.tsv
│   │   ├── prefer-visible-col.tsv
│   │   ├── pull2140.tsv
│   │   ├── rank-sheetrank-sorted-cols.tsv
│   │   ├── record-aggr.tsv
│   │   ├── remove-errors.tsv
│   │   ├── rows-select-expr.tsv
│   │   ├── save-benchmarks.csv
│   │   ├── save-benchmarks.html
│   │   ├── save-benchmarks.json
│   │   ├── save-benchmarks.jsonl
│   │   ├── save-benchmarks.md
│   │   ├── save-benchmarks.npy
│   │   ├── save-benchmarks.rst
│   │   ├── save-benchmarks.txt
│   │   ├── save-geojson.geojson
│   │   ├── save-json.csv
│   │   ├── save-json.html
│   │   ├── save-json.json
│   │   ├── save-json.jsonl
│   │   ├── save-json.md
│   │   ├── save-json.tsv
│   │   ├── save-json.txt
│   │   ├── save-jsonla.jsonla
│   │   ├── save-usv.usv
│   │   ├── setcol_fake.tsv
│   │   ├── setcol_precision_less.tsv
│   │   ├── setcol_precision_more.tsv
│   │   ├── sort-levels.tsv
│   │   ├── sortorder.tsv
│   │   ├── sqlite_withoutrowid.tsv
│   │   ├── stdin-replay.tsv
│   │   ├── stdin.tsv
│   │   ├── sum-freq-table.tsv
│   │   ├── test_quartiles.tsv
│   │   ├── transform-cols.tsv
│   │   ├── type-customdate.tsv
│   │   ├── unfurl-dict.tsv
│   │   ├── unfurl-empty-false.tsv
│   │   ├── unfurl-empty.tsv
│   │   ├── unfurl-list.tsv
│   │   ├── xlsx-color-cells.tsv
│   │   ├── xlsx-empty-cell.tsv
│   │   ├── xlsx-header.tsv
│   │   └── xlsx-merged-cells.tsv
│   ├── graph-cursor-nosave.vd
│   ├── graph-sincos-nosave.vdj
│   ├── graphpr-nosave.vd
│   ├── hide-uniform.vdj
│   ├── histogram.vd
│   ├── import-python.vd
│   ├── inner-join.vd
│   ├── invalid_unicode_sqlite.vd
│   ├── issue1308.vdx
│   ├── issue1346.html
│   ├── issue1346.vdx
│   ├── issue1377.vdj
│   ├── issue1377b.vdj
│   ├── issue2015.vdj
│   ├── issue2190.vdj
│   ├── issue2225-nosave.vdx
│   ├── issue2227.html
│   ├── issue2227.vdx
│   ├── issue2316.vd
│   ├── issue2476.vdj
│   ├── issue2524-curcol.vdj
│   ├── issue2890-parquet-addrow-nosave.vdx
│   ├── issue2901-defer-edit-nosave.vdx
│   ├── issue3015-nosave.vdx
│   ├── issue3022-nosave.vdx
│   ├── issue350.json
│   ├── issue350.vdj
│   ├── issue655.vdx
│   ├── issue733.vd
│   ├── issue964-inner-join.vd
│   ├── issue964a.csv
│   ├── issue964b.csv
│   ├── join-cols-single-sheet.vd
│   ├── join-concat.vdj
│   ├── join-different-types.vd
│   ├── join-extend-mult.vd
│   ├── join-merge-1.jsonl
│   ├── join-merge-2.jsonl
│   ├── join-merge.vd
│   ├── join-non-unique-cols.vd
│   ├── joining_error.xlsx
│   ├── joining_error_interesting_records.csv
│   ├── known-broken.vdx
│   ├── listofdictobj.vd
│   ├── load-2d-matrix.vdj
│   ├── load-conllu.vdj
│   ├── load-dir.vd
│   ├── load-fec.vdj
│   ├── load-fixed-header0.vdx
│   ├── load-fixed-leadingspaces.vdx
│   ├── load-fixed.vd
│   ├── load-grep-json.vd
│   ├── load-grep-standard.vd
│   ├── load-h5.vd
│   ├── load-html.vd
│   ├── load-http-flaky.vd
│   ├── load-json.vd
│   ├── load-jsonla.vdj
│   ├── load-lsv.vd
│   ├── load-msgpack.vd
│   ├── load-msgpackz.vd
│   ├── load-numpy.vdj
│   ├── load-ods.vd
│   ├── load-pandas-2.vd
│   ├── load-pandas-3.vd
│   ├── load-pandas.vd
│   ├── load-parquet.vd
│   ├── load-png.vdj
│   ├── load-sqlite-view.vd
│   ├── load-sqlite.vd
│   ├── load-stata.vd
│   ├── load-toml.vd
│   ├── load-usv.vd
│   ├── load-xlsx.vd
│   ├── load-xml.vdj
│   ├── load-xpt-n311.vdj
│   ├── load-yaml.vd
│   ├── load-zip.vd
│   ├── load_npy.vdj
│   ├── long-title-xlsx-nosave.vd
│   ├── macros/
│   │   ├── golden/
│   │   │   └── test_macro.tsv
│   │   └── test_macro.vd
│   ├── melt-error.vd
│   ├── messenger-nosave.vd
│   ├── monthly-revenue.vd
│   ├── numeric-cols.vdj
│   ├── numeric-names.vd
│   ├── numeric_binning.vd
│   ├── outer-join-1.vd
│   ├── outer-join-2.vd
│   ├── output/
│   │   └── .gitignore
│   ├── pandas_loader_dup_selected.vd
│   ├── petsdiet.vd
│   ├── pivot-error.vd
│   ├── pivot-noaggr.vd
│   ├── pivot.vdj
│   ├── pr2302.vdj
│   ├── pr2308.vdj
│   ├── pr2372.tsv
│   ├── pr2372.vdj
│   ├── pr2400.vdj
│   ├── pr2614.vdj
│   ├── pr2647.vdj
│   ├── pr2688a.vdj
│   ├── pr2688b.vdj
│   ├── pr2815.vdj
│   ├── pr2855.vdj
│   ├── prefer-visible-col.vd
│   ├── pull2140.vdj
│   ├── quantum-sum-manual.vdj
│   ├── quit-perf.vdj
│   ├── rank-sheetrank-sorted-cols.vdj
│   ├── record-aggr.vd
│   ├── remove-errors.vd
│   ├── rows-select-expr.vd
│   ├── save-benchmarks.vd
│   ├── save-geojson.vd
│   ├── save-json.vd
│   ├── save-jsonla.vdj
│   ├── save-usv.vd
│   ├── setcol_fake.vdj
│   ├── setcol_precision_less.vdj
│   ├── setcol_precision_more.vdj
│   ├── small.json
│   ├── sort-levels.tsv
│   ├── sort-levels.vd
│   ├── sortorder.vdj
│   ├── sqlite_withoutrowid.vd
│   ├── sum-freq-table.vd
│   ├── test-delimiter.sh
│   ├── test-macros.sh
│   ├── test-perf.sh
│   ├── test-pytest.sh
│   ├── test-roundtrip.sh
│   ├── test-smoke.sh
│   ├── test-startpos.sh
│   ├── test-startup-time.sh
│   ├── test-stdin-replay.sh
│   ├── test-stdin.sh
│   ├── test-vdx.sh
│   ├── test-zsh-syntax.sh
│   ├── test_quartiles.vd
│   ├── testenv.sh
│   ├── transform-cols.vd
│   ├── type-customdate.vdj
│   ├── unfurl-dict.vd
│   ├── unfurl-empty-false.vd
│   ├── unfurl-empty.jsonl
│   ├── unfurl-empty.vd
│   ├── unfurl-list.vd
│   ├── xdg/
│   │   └── data/
│   │       └── visidata/
│   │           ├── 1.vdj
│   │           └── macros.jsonl
│   ├── xlsx-color-cells.vd
│   ├── xlsx-empty-cell.vd
│   ├── xlsx-header.vd
│   └── xlsx-merged-cells.vd
└── visidata/
    ├── __init__.py
    ├── __main__.py
    ├── _input.py
    ├── _open.py
    ├── _types.py
    ├── _urlcache.py
    ├── aggregators.py
    ├── apps/
    │   ├── __init__.py
    │   ├── galcon/
    │   │   ├── Dockerfile.galcon-client
    │   │   ├── Dockerfile.galcon-server
    │   │   ├── README.md
    │   │   ├── galcon-server.py
    │   │   ├── galcon.py
    │   │   ├── requirements.txt
    │   │   └── setup.py
    │   ├── vdsql/
    │   │   ├── .gitignore
    │   │   ├── CHANGELOG.md
    │   │   ├── MANIFEST.in
    │   │   ├── README.md
    │   │   ├── __about__.py
    │   │   ├── __init__.py
    │   │   ├── __main__.py
    │   │   ├── _ibis.py
    │   │   ├── bigquery.py
    │   │   ├── clickhouse.py
    │   │   ├── demos/
    │   │   │   └── clickhouse-demo.vdx
    │   │   ├── requirements-extra.txt
    │   │   ├── requirements.txt
    │   │   ├── setup.py
    │   │   ├── snowflake.py
    │   │   ├── test.sh
    │   │   ├── tests/
    │   │   │   ├── dup-limit.vdj
    │   │   │   ├── freq-open-selected.vdj
    │   │   │   ├── freq.vdj
    │   │   │   ├── golden/
    │   │   │   │   ├── dup-limit.jsonl
    │   │   │   │   ├── freq-open-selected.jsonl
    │   │   │   │   ├── freq.jsonl
    │   │   │   │   ├── scoop.jsonl
    │   │   │   │   ├── select-col-regex.jsonl
    │   │   │   │   ├── select-expr-cast-type.tsv
    │   │   │   │   ├── select-expr.jsonl
    │   │   │   │   ├── select-row-dup.jsonl
    │   │   │   │   ├── toggle.jsonl
    │   │   │   │   ├── unselect-regex.tsv
    │   │   │   │   └── unselect.jsonl
    │   │   │   ├── scoop.vdj
    │   │   │   ├── select-col-regex.vdj
    │   │   │   ├── select-expr-cast-type.vdj
    │   │   │   ├── select-expr.vdj
    │   │   │   ├── select-row-dup.vdj
    │   │   │   ├── toggle.vdj
    │   │   │   ├── unselect-regex.vdj
    │   │   │   └── unselect.vdj
    │   │   └── vdsql
    │   └── vgit/
    │       ├── CHANGELOG.md
    │       ├── README.md
    │       ├── USECASES.md
    │       ├── __init__.py
    │       ├── __main__.py
    │       ├── abort.py
    │       ├── blame.py
    │       ├── branch.py
    │       ├── config.py
    │       ├── diff.py
    │       ├── gitsheet.py
    │       ├── grep.py
    │       ├── log.py
    │       ├── main.py
    │       ├── remote.py
    │       ├── repos.py
    │       ├── setup.py
    │       ├── stash.py
    │       ├── status.py
    │       ├── statusbar.py
    │       ├── tests/
    │       │   ├── git_branch_test.vdx
    │       │   └── git_remote_test.vdx
    │       ├── vgit
    │       └── vgit-guide.md
    ├── basesheet.py
    ├── bezier.py
    ├── canvas.py
    ├── canvas_text.py
    ├── choose.py
    ├── clean_names.py
    ├── clipboard.py
    ├── cliptext.py
    ├── cmdlog.py
    ├── color.py
    ├── column.py
    ├── ddw/
    │   ├── input.ddw
    │   └── regex.ddw
    ├── ddwplay.py
    ├── deprecated.py
    ├── desktop/
    │   ├── org.visidata.VisiData.metainfo.xml
    │   └── visidata.desktop
    ├── editor.py
    ├── errors.py
    ├── experimental/
    │   ├── __init__.py
    │   ├── daw/
    │   │   ├── CLAUDE.md
    │   │   ├── README.md
    │   │   ├── TODO.md
    │   │   ├── __init__.py
    │   │   ├── merge_transcripts.py
    │   │   ├── mpv.py
    │   │   ├── vdaw.py
    │   │   └── xmd2json.py
    │   ├── diff_sheet.py
    │   ├── digit_autoedit.py
    │   ├── gdrive.py
    │   ├── google.py
    │   ├── gsheets.py
    │   ├── helloworld.py
    │   ├── live_search.py
    │   ├── liveupdate.py
    │   ├── llm.py
    │   ├── mark.py
    │   ├── noahs_tapestry/
    │   │   ├── __init__.py
    │   │   ├── clues.json
    │   │   ├── flame.ddw
    │   │   ├── menorah.ddw
    │   │   ├── puzzle0.md
    │   │   ├── puzzle1.md
    │   │   ├── puzzle2.md
    │   │   ├── puzzle3.md
    │   │   ├── puzzle4.md
    │   │   ├── puzzle5.md
    │   │   ├── puzzle6.md
    │   │   ├── puzzle7.md
    │   │   ├── puzzle8.md
    │   │   ├── solutions.json
    │   │   ├── tapestry.ddw
    │   │   └── tapestry.py
    │   ├── rownum.py
    │   ├── slide_cells.py
    │   ├── sort_selected.py
    │   └── vimcompat.py
    ├── expr.py
    ├── extensible.py
    ├── features/
    │   ├── __init__.py
    │   ├── addcol_audiometadata.py
    │   ├── addcol_histogram.py
    │   ├── canvas_save_svg.py
    │   ├── change_precision.py
    │   ├── cmdpalette.py
    │   ├── colorbrewer.py
    │   ├── colorsheet.py
    │   ├── command_server.py
    │   ├── currency_to_usd.py
    │   ├── customdate.py
    │   ├── dedupe.py
    │   ├── describe.py
    │   ├── expand_cols.py
    │   ├── fill.py
    │   ├── freeze.py
    │   ├── go_col.py
    │   ├── graph_seaborn.py
    │   ├── graph_zoom_y.py
    │   ├── hint_types.py
    │   ├── hlsearch.py
    │   ├── icon.py
    │   ├── incr.py
    │   ├── join.py
    │   ├── known_cols.py
    │   ├── layout.py
    │   ├── melt.py
    │   ├── normcol.py
    │   ├── open_config.py
    │   ├── open_syspaste.py
    │   ├── ping.py
    │   ├── procmgr.py
    │   ├── pypkg.py
    │   ├── random_sample.py
    │   ├── rank.py
    │   ├── regex.py
    │   ├── reload_every.py
    │   ├── rename_col_cascade.py
    │   ├── repeat.py
    │   ├── repl.py
    │   ├── replay_bulk.py
    │   ├── scroll_context.py
    │   ├── select_equal_selected.py
    │   ├── setcol_fake.py
    │   ├── slide.py
    │   ├── sparkline.py
    │   ├── status_source.py
    │   ├── sysedit.py
    │   ├── sysopen_mailcap.py
    │   ├── term_extras.py
    │   ├── transpose.py
    │   ├── type_ipaddr.py
    │   ├── type_url.py
    │   ├── unfurl.py
    │   └── window.py
    ├── form.py
    ├── freqtbl.py
    ├── fuzzymatch.py
    ├── graph.py
    ├── guide.py
    ├── guides/
    │   ├── AggregatorsSheet.md
    │   ├── ClipboardGuide.md
    │   ├── ColumnsGuide.md
    │   ├── CommandsSheet.md
    │   ├── DirSheet.md
    │   ├── ErrorsSheet.md
    │   ├── FrequencyTable.md
    │   ├── GrepSheet.md
    │   ├── JsonSheet.md
    │   ├── MacrosSheet.md
    │   ├── MeltGuide.md
    │   ├── MemorySheet.md
    │   ├── MenuGuide.md
    │   ├── ModifyGuide.md
    │   ├── MovementGuide.md
    │   ├── PivotGuide.md
    │   ├── RegexGuide.md
    │   ├── SelectionGuide.md
    │   ├── SlideGuide.md
    │   ├── SortGuide.md
    │   ├── SplitpaneGuide.md
    │   ├── TypesSheet.md
    │   ├── WindowFunctionGuide.md
    │   └── XsvGuide.md
    ├── help.py
    ├── hint.py
    ├── indexsheet.py
    ├── input_history.py
    ├── interface.py
    ├── keys.py
    ├── loaders/
    │   ├── __init__.py
    │   ├── _pandas.py
    │   ├── api_airtable.py
    │   ├── api_matrix.py
    │   ├── api_reddit.py
    │   ├── api_zulip.py
    │   ├── archive.py
    │   ├── arrow.py
    │   ├── claude.py
    │   ├── conll.py
    │   ├── csv.py
    │   ├── eml.py
    │   ├── f5log.py
    │   ├── fec.py
    │   ├── fixed_width.py
    │   ├── frictionless.py
    │   ├── geojson.py
    │   ├── google.py
    │   ├── graphviz.py
    │   ├── grep.py
    │   ├── hdf5.py
    │   ├── html.py
    │   ├── http.py
    │   ├── imap.py
    │   ├── jrnl.py
    │   ├── json.py
    │   ├── jsonla.py
    │   ├── lsv.py
    │   ├── mailbox.py
    │   ├── markdown.py
    │   ├── mbtiles.py
    │   ├── msgpack.py
    │   ├── mysql.py
    │   ├── npy.py
    │   ├── odf.py
    │   ├── orgmode.py
    │   ├── pandas_freqtbl.py
    │   ├── parquet.py
    │   ├── pcap.py
    │   ├── pdf.py
    │   ├── png.py
    │   ├── postgres.py
    │   ├── psv.py
    │   ├── rec.py
    │   ├── s3.py
    │   ├── sas.py
    │   ├── scrape.py
    │   ├── shp.py
    │   ├── spss.py
    │   ├── sqlite.py
    │   ├── texttables.py
    │   ├── toml.py
    │   ├── tsv.py
    │   ├── ttf.py
    │   ├── unzip_http.py
    │   ├── usv.py
    │   ├── vcf.py
    │   ├── vds.py
    │   ├── vdx.py
    │   ├── xlsb.py
    │   ├── xlsx.py
    │   ├── xml.py
    │   ├── xword.py
    │   └── yaml.py
    ├── macos.py
    ├── macros.py
    ├── main.py
    ├── mainloop.py
    ├── man/
    │   ├── parse_options.py
    │   └── vd.inc
    ├── memory.py
    ├── menu.py
    ├── metasheets.py
    ├── modify.py
    ├── motd.py
    ├── mouse.py
    ├── movement.py
    ├── optionssheet.py
    ├── path.py
    ├── pivot.py
    ├── plugins.py
    ├── pyobj.py
    ├── rename_col.py
    ├── save.py
    ├── search.py
    ├── selection.py
    ├── settings.py
    ├── sheets.py
    ├── shell.py
    ├── sidebar.py
    ├── sort.py
    ├── statusbar.py
    ├── stored_list.py
    ├── tests/
    │   ├── __init__.py
    │   ├── benchmark.csv
    │   ├── conftest.py
    │   ├── sample.tsv
    │   ├── test_cliptext.py
    │   ├── test_commands.py
    │   ├── test_completer.py
    │   ├── test_date.py
    │   ├── test_edittext.py
    │   ├── test_features.py
    │   ├── test_fixed_width.py
    │   ├── test_keystrokes.py
    │   ├── test_menu.py
    │   ├── test_parsepos.py
    │   └── test_path.py
    ├── text_source.py
    ├── textsheet.py
    ├── theme.py
    ├── themes/
    │   ├── __init__.py
    │   ├── adwaita.py
    │   ├── ascii8.py
    │   ├── asciimono.py
    │   └── light.py
    ├── threads.py
    ├── tuiwin.py
    ├── type_currency.py
    ├── type_date.py
    ├── type_floatsi.py
    ├── undo.py
    ├── utils.py
    ├── vdobj.py
    ├── vendor/
    │   ├── __init__.py
    │   └── appdirs.py
    ├── windows.py
    └── wrappers.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .codespellrc
================================================
[codespell]
skip = .git,*.pdf,*.svg,*.tsv,man,formats.jsonl,sample_data,*.cast,golden
# some ad-hoc or less common word spellings or vars used
# `F`requency and such
ignore-regex = `[A-Z]`[a-z]+\b
ignore-words-list = vertexes,wil,ned,parms,datas,sav,inport,miniscule


================================================
FILE: .devcontainer/Dockerfile
================================================
FROM python:3


================================================
FILE: .devcontainer/devcontainer.json
================================================
{
    "name": "VisiData",
    "dockerFile": "Dockerfile",
    "extensions": [
        "ms-python.python"
    ],
    "postCreateCommand": ".devcontainer/post-create.sh"
}


================================================
FILE: .devcontainer/launch.json
================================================
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: vd launch",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}/bin/vd",
            "args": [
                "-f",
                "pandas",
                "${workspaceFolder}/sample_data/benchmark.csv"
            ],
            "console": "integratedTerminal"
        },
        {
            "name": "Python: vd attach",
            "type": "python",
            "request": "attach",
            "processId": "${command:pickProcess}"
        }
    ]
}


================================================
FILE: .devcontainer/post-create.sh
================================================
#!/bin/bash

make install-dev setup-vscode


================================================
FILE: .devcontainer/settings.json
================================================
{
    "python.pythonPath": "/usr/local/bin/python",
    "python.testing.pytestArgs": [
        "visidata"
    ],
    "python.testing.unittestEnabled": false,
    "python.testing.nosetestsEnabled": false,
    "python.testing.pytestEnabled": true
}


================================================
FILE: .gitattributes
================================================
www/asciinema-player.js binary


================================================
FILE: .github/FUNDING.yml
================================================
github: saulpw
patreon: saulpw


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create an issue if anything doesn't appear to be working right.
title: ''
labels: bug
assignees: ''

---

**Small description**

**Data to reproduce**

[Please include a small amount of sample data that demonstrates the issue.]

**Steps to reproduce**

[If the issue takes more than a few commands, please attach a [saved commandlog](http://visidata.org/docs/save-restore/).]

**Expected result**

**Actual result with screenshot**

[If there is an error message, please include the full stack trace shown with `Ctrl+E` or `vd --debug`.]

**Configuration**

- Does this issue reproduce without any plugins or configuration (using the `-N` CLI flag)?

- Does this issue reproduce with either the [latest release](https://www.visidata.org/releases/), or with the [develop branch](https://www.visidata.org/install/#update-visidata-from-an-existing-installation)?

**Additional context**

- What platform and version are you using (Linux, MacOS, Windows)?

- Which version of Python?

- Which terminal are you using (for display and input issues)?


================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.md
================================================
---
name: Feature request
about: Provide feedback on how you want VisiData to grow in the future.
title: '[] '
labels: wishlist
assignees: ''

---

We want to hear your ideas about where VisiData can go and anything you think could be made smoother.
We consider all ideas that are feasible, but please don't be offended if we close it--we generally limit our open issues to bugs and small concrete feature requests that are part of the planned roadmap.

If you want to propose a command, please include:

- [] what should the helpstring say
- [] where should this command go in the menu system


================================================
FILE: .github/ISSUE_TEMPLATE/loader-request.md
================================================
---
name: Loader request
about: Suggest a loader that supports a new data format
title: "[loader] "
labels: feature request
assignees: ''

---

We love to build loaders with people that are familiar with the format and how it is used.  Knowing about quirks and variations and usage-in-the-wild is very hepful.

To that end, a new loader can be created quite quickly with the following:

1) A small sample representative dataset in that format.

2) A basic Python script which queries and dumps the data (possibly using an external package).



================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: Question
about: Ask a question you have about VisiData.
title: '[question]'
labels: question
assignees: ''

---

If you think your question would benefit from input from anyone in the community (e.g. 'How do I X in VisiData') consider [opening a Discussion instead](https://github.com/saulpw/visidata/discussions/new/choose).

If you think it would benefit from a more synchronised back-and-forth with Saul, consider joining us in libera.chat (#visidata) or [discord](https://bluebird.sh/chat).



================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
- [ ] If contributing a core loader, [the loader checklist](https://visidata.org/docs/contributing#loader) was referenced.
- [ ] If registering an external plugin, [the plugin checklist](https://visidata.org/docs/contributing#plugins) was referenced.


================================================
FILE: .github/workflows/main.yml
================================================
name: visidata-ci-build
on:
  pull_request:
    branches:
      - stable
      - develop
  push:
    branches:
    - stable
    - develop

jobs:
  full-tests:

    strategy:
      matrix:
        python-version: ["3.9", "3.14"]

    runs-on: ubuntu-slim
    timeout-minutes: 20
    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'

    - name: Install VisiData with test dependencies
      run: make install-test

    - name: Run tests
      run: make test

  unit-tests:

    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13"]

    runs-on: ubuntu-slim
    timeout-minutes: 10
    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'

    - name: Install VisiData with test dependencies
      run: make install-test

    - name: Run unit tests
      run: make test


================================================
FILE: .github/workflows/vdsql.yml
================================================
name: vdsql-testing
on:
  pull_request:
    branches:
      - stable
      - develop
  push:
    branches:
      - stable
      - develop

jobs:
  run-tests:

    strategy:
      matrix:
        python-version: ["3.10", "3.14"]

    runs-on: ubuntu-slim
    steps:
    - uses: actions/checkout@v2

    - name: Set up Python ${{ matrix.pythonversion }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install VisiData
      run: make install

    - name: Install vdsql
      run: pip3 install visidata/apps/vdsql/

    - name: Ensure it starts up
      run: vdsql --version

    - name: Run tests
      run: make test-vdsql


================================================
FILE: .github/workflows/vgit.yml
================================================
name: vgit-testing
on:
  pull_request:
    branches:
      - stable
      - develop
  push:
    branches:
      - stable
      - develop

jobs:
  run-tests:

    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11"]

    runs-on: ubuntu-slim
    steps:
    - uses: actions/checkout@v2

    - name: Set up Python ${{ matrix.pythonversion }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install VisiData
      run: make install

    - name: Install vgit
      run: pip3 install visidata/apps/vgit/

    - name: Ensure it starts up
      run: vgit --version

    - name: Create visidatarc
      run: touch ~/.visidatarc

    - name: Import vgit
      run: echo "import visidata.apps.vgit" >> ~/.visidatarc

    - name: Run tests
      run: make test-vgit


================================================
FILE: .gitignore
================================================
__pycache__
.tox/
.idea/
.vscode/
visidata.egg-info/
*.egg-info/
*v_env*
*~
*.swp
*.swo
*dist
*_build
build/
tags
/tests/log/
# Mac files
.DS_Store
.direnv
.envrc
.meta

# Generated man pages (run `make man` to build)
visidata/man/vd.1
visidata/man/visidata.1
visidata/man/vd.txt
docs/man.md


================================================
FILE: .gitmodules
================================================
[submodule "deps/pyxlsb"]
	path = deps/pyxlsb
	url = https://github.com/saulpw/pyxlsb.git

[submodule "deps/requests-cache"]
	path = deps/requests-cache
	url = https://github.com/saulpw/requests-cache.git


================================================
FILE: .gitpod.Dockerfile
================================================
FROM python:3


================================================
FILE: .gitpod.yml
================================================
image:
  file: .gitpod.Dockerfile

tasks:
  - init: pip install -r ./dev/requirements-dev.txt && pip install -e .


================================================
FILE: .mailmap
================================================
# .mailmap - Consolidate git author identities
# See: https://git-scm.com/docs/gitmailmap
#
# Format: Canonical Name <canonical@email.com> Old Name <old@email.com>
# Lines map alternative identities to canonical form for git shortlog, blame, etc.

# AJ Kerrigan
AJ Kerrigan <kerrigan.aj@gmail.com> <aj@speckledmonkey.com>
AJ Kerrigan <kerrigan.aj@gmail.com> <akerrigan@mdsol.com>

# Anja Kefala
Anja Kefala <anja.kefala@gmail.com> anjakefala <anja.kefala@gmail.com>
Anja Kefala <anja.kefala@gmail.com> anjakefala <anja@voltrondata.com>
Anja Kefala <anja.kefala@gmail.com> Kefala <anja.kefala@gmail.com>
Anja Kefala <anja.kefala@gmail.com> <anja@voltrondata.com>

# David Branner (was misconfigured as "Your Name")
David Branner <brannerchinese@users.noreply.github.com> Your Name <brannerchinese@users.noreply.github.com>

# geekscrapy (also committed as molley)
geekscrapy <github.7rhdeqzfu7@pkt5.com> molley <github.7rhdeqzfu7@pkt5.com>
geekscrapy <github.7rhdeqzfu7@pkt5.com> <github.7rhdeqzfu.7@pkt5.com>

# pacien
pacien <pacien.trangirard@pacien.net> <pacien@users.noreply.github.com>

# Saul Pwanson
Saul Pwanson <code@saul.pw> saulpw <code@saul.pw>
Saul Pwanson <code@saul.pw> <saulpwanson@gmail.com>
Saul Pwanson <code@saul.pw> <saul@voltrondata.com>


================================================
FILE: .theia/settings.json
================================================
{
    "python.testing.pytestArgs": [
        "visidata"
    ],
    "python.testing.unittestEnabled": false,
    "python.testing.nosetestsEnabled": false,
    "python.testing.pytestEnabled": true
}


================================================
FILE: AGENTS.md
================================================
# Repository Guidelines

## Start Here (Progressive Disclosure)
- Read `CLAUDE.md` first for the primary contributor workflow and architecture context.
- Use focused deep dives only when needed:
  - `dev/STYLE.md` for coding patterns and API conventions.
  - `dev/GIT.md` for commit and branch practices.
  - `dev/DOCS.md` for documentation syntax and style.
  - `dev/PERFORMANCE.md` for profiling and optimization work.
- Human gate is required: all AI-assisted code must be reviewed, approved, and tested by a human before merge or PR.

## Project Structure
- `visidata/`: main package (`features/`, `loaders/`, `apps/`, `themes/`).
- `tests/`: functional replay tests (`.vd/.vdj/.vdx`) and `tests/golden/` outputs.
- `visidata/tests/`: Python unit tests.
- `docs/` and `dev/`: user and developer documentation.

## Build, Test, and Development Commands
- `python3 -m pip install .`: install local package.
- `python3 -m pip install ".[test]"`: install optional test dependencies.
- `vd --version`: startup sanity check.
- `pytest -sv visidata/tests/`: run unit tests.
- `dev/test.sh -j 4`: run functional cmdlog tests and compare `tests/golden/`.
- `vd . --batch`: quick load smoke test.

## Coding and Command Conventions
- Follow `dev/STYLE.md` for naming, quoting, decorators, and sheet/column patterns.
- Use the canonical command pattern shown in `CLAUDE.md`:
  - `BaseSheet.addCommand('', 'command-name', 'code', 'help text')`
- For class placement decisions and exceptions, defer to `dev/STYLE.md`.

## Testing Guidelines
- Update `tests/*.vd*` for workflow-visible behavior changes.
- Update `visidata/tests/` for isolated Python logic.
- Before PR: run `pytest -sv visidata/tests/` and `dev/test.sh -j 4`.

## Commit & Pull Request Guidelines
- Base PRs on `develop` (not `stable`).
- Follow existing short, imperative subjects; optional scope prefixes like `[test]`, `[docs]`, `[vdsql]`.
- Keep commits focused and include tests/docs for behavior changes.
- PRs should include problem, approach, risks, and linked issues; add screenshots/gifs and reproducible `.vd` scripts for UI changes.
- Follow CAA and copyright assignment requirements in `CONTRIBUTING.md`.


================================================
FILE: CHANGELOG.md
================================================
# VisiData version history

# v3.4 (unreleased)

Thanks to @midichef for many bugfixes and improvements.

## New Features

- [commands] add `define-command` and `document` for current row (#655)
- [repl] add embedded ptipython REPL (#2736)
- [edit] add `vd.editCellBindings` for user-customizable cell editing keybindings (#2986)
- [keys] prettykeys overhaul: all bindings now use human-readable key names (#2594)
- [selection] add `select-to-prev-selected` and `select-to-next-selected` commands
- [profile] add `--profile` flag for main thread profiling
- [vimcompat] add vimcompat experiment with go-page-half bindings @daviewales (#2927)
- [packages] add Python packages sheet
- [daw] add vdaw: experimental transcript-based audio editor
- [colors] add conversion functions to/from xterm256

## Improvements

- [column] add `color_readonly` option; better status for non-writable columns (#2936)
- [column] mark expanded columns and custom columns as writable (#2936)
- [edit-cell] fail early on readonly column (#2936)
- [input] add Ctrl+Bksp, Alt+Bksp, Alt+d bindings (#2500)
- [keys] add Ctrl+Shift+Up/Down and Alt+NumPad prettykeys
- [cmdlog] expose CommandLogJsonl (#2940)
- [indexsheet] bind cancel row to zCtrl+C (#2938)
- [menu] add show-cursor and show-expr (#2848)
- [dedupe] enable custom sheet name suffixes
- [incr] use setValuesTyped to allow setting of non-numeric columns
- [modify] add Progress to setValuesTyped
- [movement] add go-screen-* commands (#2797)
- [graph] accept refline input for multiple xcols; better error checking
- [graph][seaborn] add title and axis labels @meestahp
- [aggregator] show summary labels in first non-aggregated column if right edge not on screen
- [types] add numtype, use for avg/median aggregators @pequiste (#2868)
- [aggregators] copy formatter/displayer col attrs to AggrColumn
- [aggregators] preserve type returned by mean/avg/median instead of coercing to int @pequiste
- [icons] show icons with sheets: Dir, FreqTable, Graph
- [canvas_text] add maxXY from darkdraw, fix g sliders
- [clipboard api] add get/setClipboardRows and get/setClipboardCols
- [help] refer to visidata clipboard as "internal clipboard" (#2864 #2865)
- [memory] move open-memos to BaseSheet
- [mouse api] add vd.enableMouse; ignore if no curses.mousemask (#2913 #2851)
- [config] respect XDG_CONFIG_HOME and XDG_CACHE_HOME on macOS @maxim-uvarov-ai-assistant
- [xdg] use user_data_dir for StoredLists like input_history (#2889)
- [statusbar] make lstatus_max truncation preserve vd markup (#2908)
- [shell] add options.max_threads for asynccache used by addcol-shell
- [asynccache/shell] make asynccache/addcol-shell thread-safe and runnable in macros and replay (#2826)
- [xlsx] add hlink/folHLink to color list (#2948)
- [syscopy] add cursorFullDisplay for complex objects (#2806)

## Loaders

- [vdsql] add postgres_schema option to show tables from multiple schemas (#2027)
- [vdsql] show SQL in sidebar (#2200)
- [vdsql] aliases for .ddb, .sqlite, .db (#2259)
- [vdsql] bump ibis version (#2410 #2682)
- [vdsql] fix join compatibility with Ibis >= 9.0 @terencelaurent (#2899)
- [airtable] update loader to work with latest pyairtable library @mplattner
- [conll] update loader to work with pyconll v4.0
- [eml/pcap] enable loaders for mhtml, cap, pcapng, ntar file types
- [html] for # of header rows, use options.header
- [mysql] fix connecting with no password @pequiste
- [http] add default user agent (#2880)

## Bugfixes

- [pandas] fix loader bugs @weichm (#2968)
- [mainloop] do not exit if rightstatus throws an exception
- [mainloop] reset numTimeouts when there are unfinishedThreads @iamleot (#2931)
- [canvas] delete in linear time instead of quadratic
- [canvas] properly handle labels containing markup
- [column] fix col width calc for data with markup
- [column] fix width that formatValue used for list/dict/tuple
- [loaders] stop truncating markup in fixed-width data
- [column] stop truncating col names holding markup
- [input] fix cursor x when editing data with markup
- [input] fix use of rowidx before it is set
- [input] fix perf pegging cpu (#2911)
- [input] allow editing header of readonly column
- [palette] when no choices match, show warning on Enter
- [palette] align choices with prompt before Tab is pressed
- [palette] clarify that TAB precedes ENTER
- [settings] prevent lockup when keystroke is same as longname
- [mainloop] for bindkey cmd, show longname in fail msg; catch fail() from execCommand
- [repeat] use queueCommand instead of replayOne (#2932)
- [popen] kill leftover processes that have NOT finished
- [exit] kill subprocesses before os._exit
- [modify] clear modified status after load to prevent overvigilant quitguard
- [cmdlog] replay commands on hidden columns (#2849)
- [cmdlog] make vdj shebang work on macOS @ilyagr (#2870)
- [status] do not show previous longname after unbound key (#2779)
- [join] fail on concat of sheets with unequal # of cols
- [sync] mark finished thread reliably with endTime
- [freqtbl] disable paste-before and paste-after
- [path] remove progress in iter (#2323)
- [path] handle filenames ending in . in Python 3.14 (#2887)
- [macro] fix vd attempts to use deleted macros @haoyeau (#2893)
- [clipboard] fix wrong api usage
- [http] fix default user agent (#2880)
- [canvas] fix division by zero when col's only value is a large float (#2884)
- [movement] fix desc of scroll-up/down/left/right amount
- [fuzzymatch] stringify non-str values
- [debug] do not avoid cleanup if options.debug
- [aggr] catch nonexistent aggr in addcol-aggregate
- [reload] reload_or_replace function should be on BaseSheet
- [open-row] allow pandas to open row as sheet (#2925)
- [postgres] unquote url.password in postgres loader @egwynn
- [theme] remove nonexistent color from light theme
- [shell] move open-row from incorrect Open toplevel menu
- [mainloop] fix undo of first user alteration to a replayed cmdlog
- [curses] fix crash on startup on NetBSD wscons tty consoles @rsmirnov90 (#2851)

## API

- [threads] add asyncsingle_queue decorator

# v3.3 (2025-09-07)

- added options.disp_help_flags; deprecated options.disp_expert
   - these feature flags are supported: cmdpalette hints nometacols guides inputkeys inputfield all
   - see helpstr for more details
   - set option to '' to turn off all help overlays

- fix: include missing files in MANIFEST.in @chenrui333

Thanks to @midichef for most of the other bugfixes and improvements.

## Improvements

- [csv] auto-detect CSV format when csv_delimiter is specified @dennisangemi
- [basesheet-] start new column names fresh for each sheet

- [form-] after confirm, Esc/^C/^Q/q show disconfirm msg

- [cmdpalette-] add instruction to use Esc to cancel
- [cmdpalette-] keep case of results, allow case-sensitive search
- [cmdpalette-] scroll choices with PageUp/PageDown
- [cmdpalette-] hide choices that do not match input

- [save-] suggest 'stdin' as filename base instead of '-'
- [sheets-] on reload, fetch fresh data for URLs
- [tar-] add cols for ext and file type desc, use name as keycol
- performance improvements
- display improvements

## Bugfixes

- [archive-] fix extracting to a chosen path
- [archive-] fix error extracting to overwrite existing file
- [archive-] fix loading zip files from inside archive files

- [cliptext-] fix display of empty markup as several blank lines
- [cliptext-] restore display translation of unprintable chars
- [sheets-] remove calcSingleColLayout use of inaccurate visibleRows

- [plugins liveupdate-] fix broken addcol-new

- [curses-] fix crash on startup on NetBSD (and probably other \*BSD) wscons tty consoles, which do not expose "mousemask" in their system curses implementations. @rsmirnov90
- [input-] fix editText for files on command line with -b -i -p (#2840)
- [plugin-] fix autoload for Python versions < 3.10

- [macro-] fix corruption when saving macros after deletion #2787
- [reload-] wait for any previous reload_rows to finish #2808
- [resize-] fix resize-col-half for new columns with no rows  #2795
- [sheet-] fix reload on sheets without col layout  #2790
- [status] fix error when no help_sidebars

## Cosmetic

- [freqtbl-] stop printing status for selection on source sheet
- [input-] handle screen resize during inputMultiple(), editCell(), input()

# API

- [threads-] add asyncsingle_queue decorator

# v3.2 (2025-06-15)

Thanks to @midichef for many bugfixes and improvements.

## Improvements

* [config] XDG for default visidata_dir #2716 (#2755)
- [dev] add Python 3.13 test coverage

- [windows] install vd.cmd #2619 @ptyork
* [windows] Enable Windows clipboard in WSL #1920 @daviewales
- [windows] update windows-curses version to 2.4.1 #2119
* [windows] fix mouse support #2676  @ptyork

- [sort] allow z[ and z] to reverse col sort dir or ignore col
- [sort] replay sort-add/-change ordering from cmdlog input arg
- [sort] add sort order to ColumnsSheet  #2649

* [loaders psv] add simple .psv loader based on Tsv sheet #2727
* [loaders numpy] support 2d matrices (#2724) @maxfl

### Commands

* [aggr] addcol-aggregate and addcol-rank-sheet
- [edit] add sysedit-cell command, using external editor
- [errors] add sysopen-error command to view vd source code in editor
- [freeze] add setcol-freeze, bind to z' #2260
- [graph] add zoom-all-y #2751
- [join] add per-jointype commands #2603
- [layout] bind g- to hide-uniform-cols #2577 #2735
- [syscopy] add syscol-colname #2760

### Options

- [draw] add options.color_multiline_bottom #2715
- [graph] add options.color_graph_refline
- [disp] Rename options.disp_pixel_random to options.disp_graph_pixel_random @cool-RR

### Tweaks

* [cmdpalette-] fix scoring of space-separated search terms
* [cmdpalette-] make fuzzy match case-insensitive
* [docs] Add WindowFunctionGuide and AggregatorSheet guides #2558 @thejud
* [theme] update light theme #2729
* [status] remove non-precious sheets from sheetlist #2573
- [input-] fix word locator for Ctrl+Right motion
- [input-] allow edit of cell in hidden column  #2749
- [errors-] make ErrorSheet/ErrorsSheet into singletons
- [threads-] make threads-all show a singleton sheet
- [threads-] allow repeated toggling of profiling
- [IndexSheet] move gC and gI to IndexSheet #2603
- [main-] enable cell editing for interactive batch mode #2639
- [loaders hdf5] guess types(hdf5), understand unsigned int type @maxfl
- [loaders archive,sqlite-] guess sqlite/tar/zip filetypes confidently
- [loaders vds-] save/restore column-specific properties via getstate/setstate #2699

## Bugfixes

- [aggr-] allow undo for aggregate-col/cols
- [aggr-] cap runtime when formatting memo status
- [canvas-] stop infinite refresh for graphs with many points
- [cmdlog-] save prev replay when starting new replay #2531
- [deprecated-] show warning when using deprecated commands  #2215
- [features-] reload_every: wait for reload before looping
- [features-] sysedit: modify cell only if editor changes value (#2656)
* [freqtbl-] fix excessive memory usage for undo of selections  #2759
- [incr-] addcol-incr-step as expected #2769
- [input-] preserve None cells on external editor quit
- [join-] fix putValue for merge rows absent in any source sheet
- [layout-] change resize-cols-max into a toggle  #2782
- [macro-] only record commands that are replayable
- [mailcap] install dead battery for mailcap in python 3.13 #2576
- [main-] prevent hang when vd -p - reads from terminal
- [npy-] fix bug truncating cols wider than window  #2783
- [open-] fix open-file for - in cmdlogs #2582
- [open-] fix opening a dir with an filetype extension
- [reload-] have reload-modified check for earlier changes  #2551
- [save-] fix error string
- [sheets-] record key-col toggle for replay as key-col-on/-off
- [sort-] fix undo when sheet has a previous ordering
- [undo-] fix undo editing cells in ValueColumn, ExpandedColumn, ColumnSourceAttr  #2765
- [vdsql-] ensure each thread in vdsql tracks its single connection

## Cosmetic

- [cliptext-] truncate sheet names handling full-width chars
- [column-] do not rjustify non-numeric values #2750
- [csv-] warn when guessed option differs from default #2690
- [dir-] sort by filename, after sorting by modtime
- [errors-] fix error-recent always showing "no error"
- [fixed loader] use empty str for null_value in fixed width sheet
- [form-] fix underlining of substrings by FormCanvas
- [help- sidebar-] prevent sidebar flicker #2630
- [help] fix duplicate help descriptions #2762
- [input-] fix editline() for characters having screen width > 1
- [input-] speed up pasting long strings into line editor
- [input-] fix off-by-1 when drawing at right edge of screen
- [main-] format vd_cli exceptions to be caretless
- [menu-] remove duplicate Help entry #2714
- [menu] replace hint with motd
- [reload-] tolerate stale columns in drawcache during allAggregators #2607 #2763
- [save-] warn if no save destination given  #2580
- [status] use sheet.icon on sheetlist #2772
- [threads-] catch error trying to start a second profiler

# v3.1 (2024-10-14)

- drop support for Python 3.7  #2231
- [vdsql] bump ibis dep to v8
- [test] fix minimum pandas version to 1.5.3

## Improvements

- [sidebar] Ctrl+G to cycle sidebar (including off) in both main and input modes #2202
- [sidebar help] rightmost statusline ({vd.sidebarStatus}) shows number of help sidebars (clickable to toggle sidebar)
- [sheet tabs] left statusline sheet tabs (clickable to jump to sheet) #2030
- [aggregators] immediate async summary of column aggregators on bottom rows #2209
- [graph] reflines at user-defined x or y #2487
- [guides] add several guides (thanks to @thejud @reagle @ajkerrigan @anjakefala)
- [expressions] much cleanup
  - `row["colname"]` to refer to a specific column by name, without needing to be a valid identifier #2539
  - `_row` is underlying row object
  - support curcol in g= and select and search expressions #2524
  - $curcol for addcol-sh
  - globals include most symbols from math by default; also datetime and date both as the vd.date subclass
  - [input] memory variables included as autocomplete keys #2509

## Tweaks

- [history] create visidata_dir (default ~/.visidata/) if not exists to enable input history by default #2298
- [cli] "-p -" replays stdin as a .vdj file
- [guide] allow front matter in guide .md files; "sheettype" metadata to associate with a sheet
- [cmdpalette] do not fuzzymatch keys that start with _
- [interface] hidden columns minutely visible past right of sheet; `_` to re-expand #2394
- [interface] record all commands in macro (even replay=False); add status indicator when recording #2435
- [interface] clickable cell error symbol
- [interface] default theme color improvements, including a slight bg glow for cursor column
- [aggregators] add keymin; handle corner cases #2308
- many small interface improvements: error messages, clarifications, edge cases, guardrails.  (thanks to @midichef for many of these)

## Options

- max_rows to stop loading early #2356
- [xlsx] add xlsx_color_cells (default: True); set to False to disable xlsx cell colorizers
- regex_skip is now replayable
- disp_multiline_focus to only expand cursor row when multiline enabled #2205
- disp_expert (int); set to 1 to enable advanced interface elements and unhide advanced columns by default; set to 5 (or greater) to disable input palette and help sidebars
- pass disp_wrap_* to textwrap for multiline display #2506
- rename note_* to disp_note_* #2381
- separate color_longname into color_longname_status and color_longname_guide

## Commands

- addcol-histogram to make histogram from any numeric column #2208
- exec-longname-simple for single-line (non-palette) input
- go-col-name to go to a column by name
- [dir] open-row-filetype to open row using the given filetype
- [graph] plot-source-selected on ColumnsSheet #2424
- [experimental] sort-selected-asc/desc #2295
- deprecate view-cell #2381
- unfurl finishes with column cursor on unfurled column

## API

- [command] addCommand(..., replay=False) to not add to cmdlog
- [sort] add Sheet.ordering property as list[tuple[Column,bool]] #2295
- [path] note nonstandard behavior of RepeatFile.read()
- [input] allow different record kwarg for different fields in inputMultiple

## Loaders

- [msgpack] new loader #2419
- [grep] new loader for output of grep/ripgrep #2443
- [csv] display loading/saving progress
- [csv] handle more dialect parameters from Sniffer
- [csv tsv] remove default regex_skip of hash lines #2458
- [eml] save email parts without filenames #2376
- [fixed] skip separating space in fixed_width loader #2255
- [json] remove column type inference #2475 #2131
- [json] warn if some cols/sheets were not saved #2199
- [markdown] use col_href as link for col
- [ods] strip empty cells/rows from end #2440
- [rec] roundtrippable
- [sas] allow edits to source sheet
- [save] derived null in text formats should be empty string #2476
- [save] handle empty delimiter when options.safety_first
- [text] do not strip lines of whitespace #2328
- [tsv] allow empty regex_skip
- [tsv] allow saving with NUL delimiters
- [tsv] if field or row delimiter is NUL, disable regex skipping
- [vds] allow sheets with no rows #2342
- [xml] save XML as utf-8 instead of utf-8-sig #2520

## Bugfixes

- [command] addcol-sh: ensure arguments are properly quoted  #2415
- [selection] make toggle thread-safe for FreqTableSheet #1671
- [undo] prevent undo of command that created sheet #2244
- [undo] cmdlog always use rowidx, not rowkey #2246
- [cliptext] fix onclick url #2466
- [mouse] fix mouse-disable  #2379
- [sort] do not modify Sheet._ordering #2254
- [bindings] do not re-prettykeys; call prettykeys for all bindkey() too #2247
- [terminal] add no-op for ANSI focus-in and focus-in #2247
- [sort] add _ordering to Sheet class instead of __init__ #2190
- [transpose] allow to set values #2548
- [sheet] make 20 tabs clickable #2030
- [macro] do not record macro inner steps #2531
- [filetype] use correct case for file extension determination #2263
- [replay] stop stderr batch progress when interactive #2251
- [replay] wait for replay to start before stderr progress #2251
- [replay] fix rare duplication of replay cmds #2392
- and many more

# v3.0.2 (2024-01-15)

### Fixes and minor improvements

- [cli] add `--guides` to open the guide index
- [cmdpalette] only first 10 suggestions should have shortcut keys  #2242
- [draw] use default bg on col hdr sep
- [input] draw help sidebar on top of sheet/updater  #2241
- [graph] sort by x and y columns when diving (PR by @midichef #2226)
- [guides] improve formatting of command and options in sidebar + guides
- [replay] stop stderr batch progress when entering interactive mode  #2251
- [shell] clean up DirSheet sidebar
- [sidebar] grab user's keystroke of sidebar-toggle for helpstring  #2250
- [sort] a better fix for maintaining sort ordering on sheet copies  #2190 #2254
- [tests] add "test" extras for installing PyPI packages needed to run tests

# v3.0.1 (2024-01-07)

### Fixes and minor improvements

- [color] add `color_longname` to use instead of `color_keystrokes`  #2219
- [columns] add `displayer` attribute to saved column state
- [cmdpalette] Enter executes first row, if partially typed out  #2219
- [cmdpalette] add sidebar for longname and aggregator palette  #2219
- [guide] add `show-command-info` to display command info for a keystroke  #2228
- [keys] add `*BtnUp` pretty keys for `BUTTON#_RELEASED`  #2219
- [dup-selected] dup-selected should unselect all rows in copied sheet  #2225
- [expr] fix KeyError crash with invalid inputs in `expr` for Python 3.12  #2179
- [help] fix columns sheet sidebar
- [loaders html] ignore parsing exceptions on invalid urls  #2227
- [sheets] limit end separators to rightmost visible column of sheet (PR by @midichef #2237)
- [sidebar] fix fmtstr for case where source is BaseSheet  #2239
- [sort] maintain ordering on sheet copies  #2190
- [test] update unit tests to use packaged sample.tsv and benchmark.csv  #2218
- [threads] do not try to cancel already finished thread #2235
- [tests] add `assert-expr` and `assert-expr-row` to evaluate Python expressions, and assert result is truthy
- [tests] parametrize feature tests (PR by @ajkerrigan #2230)

# v3.0 (2023-12-30)

- [reorg] move independent modules into visidata/{features|experimental|themes}
- [sidebar] add sidebar  #2064
    - #1733 for full description/discussion
    - will contain a stack list of status messages
    - color syntax is [:bold]footext[/]
        - supports `[:code]`, `[:bold]`, `[:italic]`, `[:underline]`, `[:onclick]` (makes a clickable link), `[heading]`
        - [:] at terminal clears; [/] at terminal pops
        - supports basic markdown for colouring
    - toggle with `sidebar-toggle` (bound to `b`)
    - make bottom msg entirely clickable
    - make dedent and header parsing standard
    - add `options.disp_sidebar`
    - make sheet name clickable to open **Sheets Stack**
    - make options clickable
    - add `open-sidebar` (bound to `gb`) to open sidebar in new sheet
- display sheet and feature help documentation in sidebar
    - added bundles of Guide Sheets
    - open Guide Sheets into a full VisiData sheet with `Enter`
- added `options.disp_help` (default: 2), an integer and to set novice/expert mode. (#1961)
    When `disp_help` is:
    - -1: status messages aren't shown
    - 0: no help is shown
    - 1: sidebar sheet help is shown
    - 2: help is shown for the input widget on the sidebar (this was condensed and moved from the former input help panel)  #2085 #1961
    - put input help on sidebar; `disp_help` from -1 to 3
    - mark options to be excluded if `disp_help` greater than `option.max_help`
    - add contextual help while editing options in **OptionsSheet**
    - `Ctrl+G` cycles input help sidebar
- [guide] add GuideIndex toc and open-guide-index
    - add guides for macros, selection, errors
- [filetype] add guesser to sniff filetype from data  #130 #1759 #1881 #1880 #1883 #1978
- add support for Python 3.12  #1934
- migrate pkg_resources to importlib_resources (PR by @zormit and @anjakefala #1968 #1911)
- [threads] no new threads while prev cmd still running  #1148
- [batch] add progress every half second to batch mode  #1182
- [replay] implemented a single-thread replay mechanism  #1575 #2102
    - much simpler architecturally, but loses the ability to pause/resume, and show 1/N progress (now just shows number of queued commands remaining).
        - replay in general should be more stable and possibly faster.
        - Ctrl+N is now no-op for similar functionality to replay-advance
            - replay-advance command itself is removed
        - removed options.replay_movement and its functionality
        - options.replay_wait (-w on CLI) now works by setting the curses timeout, which will affect the display update frequency for all async commands.  previously was hard-coded.  try -w 0.001 while loading a big file to see a very rapid update.
- add options.overwrite='n' mode  #1805
    - replace `options.confirm_overwrite` with `options.overwrite`, which can be
      'n' (no/never), 'y' (yes/always), 'c' (confirm)
      - add vd.confirmOverwrite()
        - inputPath/inputFilename will not suggest the existing value or complete filenames if overwrite not y or c
        - add --ro/--readonly cli flag (opposite of existing -y)
        - add [RO] readonly marker on right status after [M] modified
- add command palette (PR by @moritz #2059 #247)
    - press Space to exec command by longname and bring up command palette
    - type to match by longname and/or description
    - uses fzf's fuzzymatch algo
    - press Tab/Shift+Tab to cycle through commands  #2154
        - Enter to execute highlighted command
        - 0-9 to execute numbered command
    - `options.disp_cmdpal_max` for number of suggestions
    - `options.color_cmdpalette` for base color
    - `chooseAggregators` and `jointype` also use palette  #1027 #2195
        - chooseAggregators still allows selection of multiple aggregators
    - `options.color_match` is color for matching chars in palette chooser
- convert input history to use StoredList  #2142
    - rename lastInputs.jsonl to input_history.jsonl  #2142
    - remove `options.input_history`
       - will automatically record if `options.visidata_dir` exists
- convert macros to use StoredList  #2142
    - cannot save macros if `options.visidata_dir` does not exist
    - rename `options.visidata_dir`/macros.tsv' to `options.visidata_dir`/macros.jsonl'
    - add vd2to3.vdx script to port from 2.x macros to 3.x macros
- [aggregators] sum uses start value from type of first value for Python 3.8+  #1996 #1999 #2009
- [build] add a .desktop for VisiData  #1738
- [choose] add type for join/aggregators history  #2075
- [cli] add `-i` to run interactive mode after batch  #1714
- [clipboard] implement a more universal paste that uses positional columns  #1377
- [columns] add `setcol-precision-more` and `-less`  #1609 #1650
    - works on float, floatsi, currency, date columns
- [commands] allow space-seperated keystrokes  #2067
- [confirm] make yes/no buttons clickable  #1740 #2075
- [cosmetic] standardize `__repr__` for sheets and columns (PR by cool-RR #2091 #1757)
- [describe] default width=10 for describe columns
- [dir] set name of '.' to current dir name  #1775
- [dir] set name relative to previously loaded directory  #1775
- [dir] get default save name from sheet name  #1775
- [dir] rename `options.dir_recurse:bool` to `options.dir_depth:int`  #1715
- [display] add `resize-height-input` (`zv`) and `resize-height-max` (`gzv`)  #1307
- [encoding] use `options.save_encoding` for lsv, geojson, texttable savers
- [encoding] change default options.encoding to utf-8-sig to detect/remove BOM  #200 #908 #909 #1711
- [expand] change default depth of expand-col(s)-depth to 0 (PR by @cool-RR #1809)
- [expand] change sampling warning to aside  #1054 #2066
- [expand] ignore non-cursor types in mixed-type columns
- [features] procmgr to view/manage processes, memory/cpu stats
- [features] ping to traceroute a hostip
- [features] add `addcol-histogram`  #2052
    - display a histogram-like column for any column of ints
- [features] add `contract-cols-depth` etc (bound to `g)`, `z)`, `gz)`  #1695
- [features] add contract-source-cols bound to )  #1702
- [features] reload-modified calls `reload_rows`, adding support for tail  #1686
- [features] add `addcol-audiometadata` from vmutagen plugin
    - adds metadata column for audio files
- [features] to **DirSheet** add `sysopen-mailcap`
    - opens using mailcap entry for current row, guessing mimetype
- [features] add `addcol-source` to add current column to source sheet  #988
- [features] incorporate dedupe plugin by @jsvine
    - `select-duplicate-rows` selects each row in current sheet that is a duplicate of a prior row
    - `dedupe-rows` pushes a new sheet with only non-duplicate rows of current sheet
- [features] incorporate normcol (by @jsvine)
    - `normalize-col-names` normalizes the names of all *non-hidden* columns
- [features] add server to listen for commands on `options.server_addr` and `options.server_port` (if set)
- [features] add Sheet.knownCols to preconfigure of columns by name #1488
    ```
    Sheet.knownCols.timestamp.type = date
    DirSheet.knownCols.directory.width = 0
    ```
- [freq] add select-first command
- [freq] base histogram width on column width  #1807
- [freq] set default disp_histogram to U+25A0 BLACK SQUARE (■)) (PR by @daviewales #1949 #1807)
    - [themes] ascii8 disp_histogram to *
- [freq] add `open-preview` for split pane of source rows at cursor  #1086
- [graph] colorbrewer palette chooser (thanks @er1kb)
- [graph] add commands to open external graph with matplotlib #1056
- [help] remove `Enter` binding for **HelpSheet** to `exec-command`
- [history] remove cmdlog_histfile
- [input] change `Ctrl+G` to toggle `options.disp_help`
- [input] add `Ctrl+N` to insert prettykeys of literal keystroke
- [input] allow percentage of starting value for input into commands
- [input] `inputMultiple` saves/remembers dict input
- [input] `Tab`/`Shift+Tab` move left/right in edit-mode like excel  #2169
- [join] allow selecting of join columns from all columns sheet  #1224
    - unbind `&` for **ColumnsSheet** `join-cols`
- [join] join now works with typed values, not display values  #2015
- [join] improve warning for typing source key columns before joining  #2117
- [join] clarify ensureLoaded status message  #2137
- [keys] add shifted function prettykeys (Ctrl+Shift+F1 etc)
- [layout] stop errors: hide-col on empty sheet, inputMultiple (PR by @midichef #1963)
- [linux] change default system clipboard cmd to wl-copy if the user is using wayland (PR by @rj1 #1763)
- [loaders] add `options.regex_skip` for text formats to allow e.g. comment skipping  #1559
    - Added default values for `regex_skip` to existing source sheets like tsv/csv/lsv/json/jsonla.
    - Use --regex-skip='' (or otherwise set the option to '') to disable this behavior.
- [loaders] add mailbox formats mbox/maildir/mmdf/babyl/mh loader (as supported by Python mailbox stdlib)
- [loaders] .jrnl format (jrnl.sh) loader+saver
- [loaders] add reddit API loader
- [loaders] add matrix API loader
- [loaders] add orgmode loader
- [loaders] add scraper
    - table of HTML elements as parsed by `beautifulsoup4`
- [loaders] add Parquet writer (PR by @mr-majkel #2053 #2044)
- [loaders] add s3 loader (built by @ajkerrigan)
    - open Amazon S3 paths and objects
- [loaders] add support for jsonla (JSONL arrays) format (PR by @daviewales #1730)  #1726
- [loaders] add zulip API loader
- [loaders] add airtable API loader
- [loaders] add .fec loader by @jsvine
- [loaders] add f5log loader by @bitwisecook
- [loaders] add a toml loader (PR by @ajkerrigan #1894 #1580 #1587)
- [loaders] add .conll loader by @polm
- [loaders html] display title/aria-label/caption/summary (PR by @midichef #2146)
- [loaders http] replace requests with urllib  #1808 #1704
- [loaders http] guess filetype based on magic bytes  #1760
- [loaders jsonl] allow slash comments (PR by @geekscrapy #2025)
- [loaders jsonl] deduce numeric column as float, not int  #2131 #1698
- [loaders png] use 2x2 unicode blocks instead of braille
- [loaders shell] allow deleting of directories unless `options.safety_first=True`  #1965
- [loaders sqlite] save list/dict as json  #1589
- [loaders sqlite] add `exec-sql` command to input query  #1719
- [loaders xlsx] add cell colorizers from source  #1718
- [loaders xlsx] add `column_letter` to meta columns
- [loaders xml] ignore comments
- [macros] allow deleting of macro with commit on **MacrosSheet**  #1569
- [macros] add longname/keystrokes to **MacrosSheet**  #1569 #1741
- [macros] add help sidebar to end macro input  #1569 #1741
- [menu] move Edit>Add-rows to Row>Add
- [menu] add `go-row-number` to menu  #1766
- [menu] move commit-sheet under File>Save
- [menu] add resize-cols-input to Columns -> Resize (PR by @njthomas #1887)
- [modules] include module name in Option/Command sheets
- [motd] default motd is "Support VisiData" instead of blank
- [mouse] onclick with url launches $BROWSER with url; add `displayer_url`  #2031
- [open] try using options.filetype for path  #1710
    - useful for configuring default filetype when reading from stdin
- [open] add `reopen-last-closed` which reopens the most recently closed sheet (PR by @cool-RR #1813) #1811
- [open-syspaste] create new table from system clipboard #1680
- [open-syspaste] enable filetype selection (PR by @daviewales #1717)
- [options] add `option.json_ensure_ascii` (default: True) (PR by @joaosousa1 #1776)  #1772
    - option for non-ASCII characters to be saved to JSON, on False will encode to utf-8
- [path] change most uses of Path.name to Path.base_stem  #2188
    - Path.name is the same as .base_stem for now
- [plugins] remove external Plugins Sheet; only show installed plugins
- [profile] dump profile to cwd when profiling enabled
- [forms] provide help text for color, encoding, encoding errors, and regex  #1961
- [reload] support tail with `reload-modified`  #1686
- [reload] add `reload-rows` which preserves existing columns + cursor position  #1655 #1683 #1663
    - [melt] refactor to support `reload-rows`  #2101
- [regex] use inputMultiple to allow changing regex_flags  #1925
- [regex] use `inputRegex` (which has regex help) for all regex commands
- [rename-col] add `options.rename_cascade`  #2088
    - if True, columns renames are cascaded into expressions
- [replay] has been refactored to be sync, instead of a separate async process  #1773 #1714
- [save] add `options.save_encoding (default: 'utf-8') to differentiate from `options.encoding` when saving a file  #1708
- [save] add saver for STATA files (PR by @raffaem #1563)
- [save] keep headers in txt as tsv if only one sheet and more than a single column  #2173
- [save] add status when save finished  #2157
- [selected] add `onclick` to "selection status" to quickly `dup-selected`
- [sheet] add `select-equal-selected` (unbound) to select rows with values in current column in already selected rows  #1327
- [sheet] add `clean-names` (unbound) to set options.clean_names on sheet and clean visible column names
- [sheet] remove left-click for sheets-stack  #2030 #1656
- [sheet] add `open-source` (bound to backtick) to open source sheet
- [setcol-fake] add `setcol-fake` (unbound) adds a column of Faker generated 'faketypes'
- [sparkline] add `addcol-sparkline` (unbound): adds a sparkline of all numeric columns
- [status] downgrade sheet "finished loading" to debug
- [tests] call all test_func(vd) defined in modules during pytest
- [tests] run all unit tests in CI
- [tests] add test for loading a directory  #1798
- [themes] add options.theme and visidata/themes directory of additional themes (light, ascii8, asciimono)  #1682  #1691
- [themes] keystrokes/code now with gray bg
- [types] add `ipaddr` and `ipnet` types`. add `type-ipaddr` and `type-ipnet` commands (unbound) (PR by @ajkerrigan #1946 #1782 #1910)
    - also add `select-supernets` (unbound) which selects rows where the CIDR block value includes the input address space
- [types] add `type-url` and `open-url`  #2031
- [types] add `type-datetime`  #1572 #1380 #397
- [ui] change menu, status, and other colors to be more visible
- [ui] add `options.disp_scroll_context` to keep *n* more lines above/below cursor on screen
- [ui] add version to menu status
- [undo] options.undo can only be set globally
- [usd] provide USD(s) function to convert string like '£300' or '205 AUD' to equivalent US$ as float
- [windows] change default system clipboard command to clip.exe
- [zip] add `sysopen-row` (`Ctrl+O`) to open file in `$EDITOR`  #1708

## experimental features (must be imported manually)

- [diff] got moved to experimental
- [inplace] optional replacement commands which update the new Column live as you write the expression
- [livesearch] add `dup-search` and `dup-search-cols` which search for regex forwards, creating a duplicate sheet with matching rows live
- [mark] mark rows to more easily move cursor to them
- [noahs] add basic structure for Noah's Tapestry data game
- [rownum] addcol-rownum and addcol-delta
- [slide-cells] shift cells in cursor row to the right
- add loaders gdrive and gsheets

# deprecated

- [dev] deprecate `col.setValueSafe` and `sheet.checkCursorNoExceptions`
- [regex] deprecate `addcol-subst` and `setcol-subst`
    - `addcol-regex-subst` and `setcol-regex-subst` use inputMultiple, instead of more fragile search/replace one-line input
- [regex] deprecate `split-col` and `capture-col`
- [keys] change ScrollWheelUp to ScrollUp etc
- [multiline] rename `visibility-sheet` to `toggle-multiline`
- [utils] deprecate `onExit` context manager

## bugfixes

- [aggregators] use statistics.median for more correct median  #1914
- [aggregators] fix cancelling of long-running aggregators  #1036
- [canvas] fix clicks on labels and unplotted canvs (PR by @midichef #1984)
- [canvas] put a max limit on y-axis label width (PR by @midichef #2177)
- [chooser] choose only exactly matching strings (PR by @daviewales #1902)
- [cli] support `options.encoding_errors` for stdin  #2047
- [clipboard] warn when pasting before copying (PR by @midichef #1793)
- [clipboard] improve error when deleting row on empty sheet (PR by @midichef #2006)
- [clipboard] save to tempfile, do not confirm
- [clipboard] save as given filetype
- [cliptext] fix double-width char display  #1918
- [cliptext] do not crash with x<0  #2138
- [cliptext] do not crash with miniscule widths  #2138
- [cmdlog] check for empty cursor column when adding a column (PR by @midichef #1783)
- [cmdlog] ensure record of global options in all cmdlogs
- [cmdlog] do not log undos for non-loggable commands  #1827
- [colorizers] fix custom colorizers showing in sheet context  #1225
- [columns] speed up getMaxWidth for wide columns (PR by @midichef #1747) #1728
- [columns] fix allColumnsSheet to ignore non-TableSheet
- [confirm] remove flicker in alacritty  #2040
- [confirm] commit on sheet without source should always confirm
- [currency] fix currency_neg option
- [curses] ignore early keys pressed before curses is initialised if Esc present  #1993
- [cursor] cursorColIndex now returns None if empty  #1803
- [deps] add requests-cache submodule to root visidata  #1748
- [dir] support '..' and resolve dirname relative to CWD  #1801
    - if user uses `open-dir-parent` outside of the CWD, switch to absolute paths
- [display] fix visibility with col.height>1
- [expr] more informative 'column not modifiable' error message  #1764
- [extensible] do not copy over existing attribute  #2190
- [fill] allow filling with values that are logically false (PR by @midichef #1794)
- [freq] fix names for openRow  #1777
- [freq] correctly group null/error values for `options.numeric_binning`  #1410
- [graph] fix graph ranges for xmax, ymax < 1 (PR by @midichef #1752)  #1673 #1697
- [graph] fix data on edges being drawn offscreen (PR by @midichef #1850)
- [graph] fix graph legend drawn too early (PR by @midichef #1980)
- [graph] fixes to various graphing edge cases (PR by @midichef #1896)
- [graph] fix top margin location and simplify y-coordinate calculation (PR by @midichef #1915)
- [graph] labels: add tick symbol, int precision, right margin (PR by @midichef #1931)
- [graph] fix legend display of full-width characters (PR by @midichef #1958)
- [graph] widen left margin to hold y-axis labels (PR by @midichef #1998)
- [graph] update fixPoint() to use inverted-y coordinates (PR by @midichef #2139 #2111)
- [index] add longname for g< and g>  #2011
- [input] fix Ctrl+T swap on empty string #1684
- [input] fix Ctrl+V with special keystrokes  #1799
- [input] erase status bar after prompt #1947
- [input] fix toggle input help  #1971 #1994
- [input] include history for unfocused items #1947
- [inputsingle-] loop until keystroke (do not timeout)
- [join] fail if differing number of keycols  #1678
- [join] fix join-merge (PR by @yphillip #1923 #1843)
- [jsonl] include all columns in first row, even if null
- [keystrokes] only check duplicate prefixes from allPrefixes  #1829
- [keys] add `Shift+Tab`
- [layout] fail if `hide-col` on empty sheet
- [loaders fixed] use maxWidth for saving if larger than column width  #1849
- [loaders fixed] don't truncate wide columns with fixed width saver (PR by @daviewales #1890)
- [loaders http] add `options.http_ssl_verify` to replace `options.http_req_verify`  #1939
- [loaders http] fix parsing link header (PR by @Midichef #1924 #1898)
- [loaders html] fix failure from colspan with only td tags (PR by @midichef #2002)
- [loaders html] prevent error when parsing an empty table (PR by @midichef #2140)
- [loaders imap] enable imap and fix folder name extraction (PR by @justin2004 #1917)
- [loaders json] include null columns in first row, in more cases (PR by @midichef #2109)
- [loaders jsonl] save None as null  #2183
- [loaders mysql] unquote password before sending to client (PR by @dufferzafar #1933)
- [loaders pandas] handle read methods that produce a list of dataframes (PR by @ajkerrigan #1990 #1986)
- [loaders parquet] stringify source to handle both URLs and local paths (PR by @ajkerrigan #1913)
- [loaders parquet] show string value for Parquet `large_string` (PR by @daviewales #2018 #2003)
- [loaders parquet] fix parquet reading from zip or s3 (PR by @takacsd #2133)
- [loaders parquet] handle parquet directories (PR by @mr-majkel #2160  #2159)
- [loaders pivot] fix missing anytype import
- [loaders postgres] quote schema and table name (PR by @isosphere #2129)
- [loaders pyobj] similar sheet names for dive-/open- and pyobj- #1988
- [loaders pyobj] do not skip properties that raise
- [loaders png] fix `rgb_to_attr` to return str colornum
- [loaders rec] support %sort; continue loading on exception  #2022
- [loaders sav] use fork of `savReaderWriter` for the sake of Python 3.10+ support  #1867
- [loaders sqlite] prevent creation of ./- file when reading from stdin (PR by @midichef #1945)
- [loaders sqlite] explicitly fail when file is not on disk
- [loaders tsv] use options.encoding for reading files
- [loaders ttf] implement `closePath()` to draw missing lines (PR by @midichef #1979)
- [loaders vds] save hidden columns also (PR by @pacien #2093 #2089)
- [loaders vds] fix 'keyerror: exprcolumn' for .vds (PR by @pacien #2036 #2045)
- [loaders vds] fix .csv to .vds conversion  #2037
- [loaders xlsx] saver now replaces illegal characters instead of aborting  #1402
- [loaders xml] silence FutureWarning from lxml (PR by @midichef #2149)
- [loaders xml] fix file loading (PR by @midichef #2148)
- [loaders yaml] support tuples in YAML files (PR by @cool-RR #1824)  #1822
- [loaders zip] fix loading and extracting files locally + remotely  #2127
- [macos] do not bind empty string to anything
- [macros] add prompt for cancelling macro  #1810 #1812
- [macros] specify a clearer message  #1810
- [macros] append newline to macros.tsv if necessary  #1569
- [macros] save file as longname/keystroke  #2084
- [main] print version string once, not twice (PR by @midichef #1837)
- [main] remove forced unload before interactive mode  #1943
- [menu] use "Alt+x" keybinding instead of "^[x"
- [metasheets] do not use options.encoding for internal sheet saving
- [misc] remove trailing commas from addCommand (PR by @midichef #1962)
- [modify] do not call saveSheets on commit
- [modify] commitMods do not call putValue for changes to added/deleted rows
    - also fix ItemColumn.putValue and AttrColumn.putValue to call parent
      Column.putValue before setting the value on the row
- [modify] confirm() overwrite on root sheet source path
- [modify] always set col.defer
- [modify] do not fail on Column.putValue if no setter
- [mouse] fix mouse-click on bottom pane
- [open] silence ResourceWarnings for unclosed files (PR by @midichef #2152)
- [options] disable adding rows (PR by @midichef #1944)
- [options] fix option editing
- [options] fix helpstr display when editing
- [paste] add new rows to sheet if necessary
- [path] set name to '.' for givenpath of '.'  #1768
- [path] fix progress bar for compression formats  #1175 #1255
- [path] fix open() using both binary and text mode (PR by @midichef #1955)
- [path] binary mode does not take newline argument
- [pyobj] fix pyobj-cell  #2001
- [regex] issue warning when no columns to add  #1778
- [regex] check for regex capture group  #1778
- [rename-col] skip gratuitous rewrites when undoing (PR by @pacien #2092)
- [replay] turn off confirm dialogs during replay
- [replay] clearCaches before moving cursor  #1773
- [replay] enable confirm in interactive batch mode (PR by @midichef #1751)
- [replay] do not push sheet if not already pushed  #1681
- [save] handle saving 0 sheets  #1720
- [save] fix confirm message on commit #2090
- [scroll-cells] do not error scrolling offscreen column  #1908
- [search] handle no rows and invalid regex  (PR by @midichef #2125)
- [settings] clear cache correctly before set
- [sheets] fix NameError for mincolidx  #1672
- [sheets] add confirm for `quit-sheet-free` (PR by @midichef #1755)
- [sheets] make sure addColumn called on all columns
    - not just dynamically-created columns
    - addColumn is needed to set .sheet and .defer, among other things
- [sheets] fix reload() for tsv sheets with key columns (PR by @midichef #1997)
- [sheets] fix recursion crash of Python >= 3.8, <3.9.10  (PR by #midichef
  #1722)  #1696
- [sheets] pop columns kwarg so raw list not set via final update() in
  constructor
- [sheets] fix slowness from adding rows during getMaxWidth (PR by @midichef
  #1982)
- [sheets] no longer insert column in draw() in debug mode (PR by @midichef
  #1995)
- [sheets] fix errors on sheets with no columns (PR by @midichef #2124)
- [sheets] fix splitcell to handle attribute/text pairs (PR by @ajkerrigan #2020)
- [sheets] recreate sort columns for copied sheet (PR by @midichef #2192 #2190)
- [shell] fix copying of a directory  #1970
- [status] fix Alt+Shift+Shift+X  #1828
- [status] update right status before exec  #996
- [status] add caller/module to statuses, and print on --debug  #2037
- [status] catch all exceptions during drawing  #2174
- [sort] show sort arrow for sort columns described by name (PR by @midichef #1876)
- [syscopy] always copy as utf-8
- [term] allow non-color term like vt102
- [threads] allow @AsyncThread funcs to have status kwarg
- [threads] remove non-sheet threads from unfinishedThreads (do not sync on them)
- [quit-sheet-free] re-entering a subsheet left using quit-sheet-free should reload the subsheet #1679
- [ui] integrate scrollfix from @geekscrapy  #1441
- [undo] ensure undo is sheet-specific for duped/copied sheets  #1780
- [undo] fix undo for first modification on a sheet-specific HelpSheet  #1820
- [undo] fix the removal of [M] (modified mark) after undo  #1800
- [undo] remove last matching cmdlog row, instead of first (PR by @midichef #2010)
- [unzip-http] fix recursion error when fetching remote zipfile (PR by @midichef #2116 #2110)
- [vdx] fix save error
- [windows] add Alt+ keybindings for powershell  #1630
- [windows] limit windows-curses version to 2.3.0 (PR by @bartbroere #1901 #1841)
    - asottile noticed this was a regression in the last windows-curses release
      in this issue: zephyrproject-rtos/windows-curses#41
- [windows] update windows-curses version to support Python 3.11  #2062
- [windows] fix syspaste (PR by @midichef #1921 #1920)
- [windows] fallback to `scr.getch` if no `scr.get_wch` #192
    - handles `AttributeError: '_curses.curses window' object has no attribute 'get_wch'`
- [windows] tempfiles must be closed before reopening  #2118
- [zsh] fix zsh completion (PR by @dbaynard #1960 #1959)

## performance

- [startup] delay import of `urllib.request`, `pkg_resources`, and
  `dateutil`
- [startup] remove `unittest.mock`
- [draw] `drawcache` make_formatter
- [test] use lambda instead of Mock for addstr
- [] `Column.formatValue` inline fmtstr
- [settings] `@lru_cache` sheet obj in `SettingsMgr._mappings`
- fastpath getitemdeep
    - fallback to getattrdeep if len(row) < index
- [sort] move `addProgress` outside of sortkey
- [parquet] cache large strings and cap at 1MB  #2003 #1068
- [modify] check col/row before isChanged
- [status] move getStatusSource into features to improve startup perf


## api

- [cli] printout gone; use `builtins.print`
- [color] use `ColorAttr` throughout  #2061 #2017
    - separate out fg/bg
    - allow bg and fg to take precedence independently
    - fixes issues with forced bg=black on sidebar for warning, and statusbar for working
- [guides] add API for getting and adding guides
    - add vd,getGuide and vd.addGuide
    - add Helpers for formatting commands and options
- [help] add HelpSheet to globals
- [hint] add hint mechanism to find best `Sheet.hint_function`
    - add hints for types
- [input] add vd.injectInput and vd.getCommandInput
- [keys] use prettykeys for allPrefixes #1592
- [menu] vd.addMenuItems with convenient string syntax
- [modify] Sheet.commitAddRow and Sheet.commitDeleteRow
- [modules] vd.importModule, vd.importSubmodules, vd.importExternal, vd.setPersistentOptions
    - add importExternal for most loaders and features  #1739 #1765
- [path] filesize can taken any Path-like
- [pivot freq] re-add FreqTableSheet and PivotSheet to globals  #1731
- [sheet] add vd.addCommand as alias for BaseSheet.addCommand
- [tests] add vd.resetVisiData
- [vdx] runvdx() to execute vdx strings
- rename vd.draw_sheet to vd.drawSheet
- change order of parameters for vd.subwindow to (x,y,w,h)
- change order of args to onMouse to x,y
- add vd.aside for a silent status message
- add GlobalsSheetsSheet to globals
- vd.queueCommand can take input,sheet,row,col kwargs  #1681
- add `@visidata.stored_property` to persist property


# v2.11.1 (2023-07-16)

- [tests] fix tests for Python >=3.11
- [path] update for Python 3.12 API (reported by @QuLogic #1934)

## Improvements and bugfixes

- [chooser] choose only exactly matching strings in chooser (PR by @daviewales #1902)
- [columns] speed up `getMaxWidth()` for wide columns, and correct some edge cases (PR by @midichef #1747)
- [freqtbl] Default `disp_histogram` to U+25A0 BLACK SQUARE (■)) (PR by @daviewales #1949)
- [loaders fixed] do not truncate wide columns with fixed-width saver (PR by @daviewales #1890)
- add missing import `copy`
- [graph] fix graph ranges for xmax, ymax < 1 (PR by @midichef #1752)
- [graph] fix data on edges being drawn offscreen (PR by @midichef #1850)
- [input] fix `Ctrl+T` swap on empty input (reported by @gfrmin #1684)
- [inputsingle] loop until keystroke (do not timeout)
- [fill] allow filling with values that are logically false (PR by @midichef #1794)
- [macos] do not bind empty string to any keybinding
- [paste] add new rows to sheet if insufficient rows
- [path Dirsheet] set name to '.' for givenpath of '.' (reported by @geekscrapy #1768)
- [path] fix progress for compressed files (reported by @bitwisecook #1255 #1175)
- [replay] clearCaches before moving cursor (reported by @mokalan #1773)
- [save] handle saving 0 sheets (reported by @reagle #1266 #1720)
- [settings] clear cache correctly before set
- [undo] fix so that undo is Sheet-specific on copied sheets (reported by @geekscrapy #1780)
- [undo] undoing `zd` now removes `[M]` (modification mark) (reported by @Freed-Wu #1800)

# v2.11 (2023-01-15)

- [ci] drop support for Python 3.6 (related to https://github.com/actions/setup-python/issues/543)
- [ci] add support for Python 3.11 (#1585)

- [dirsheet] add `open-dir-parent` (bound to backtick)
- [join] add new "concat" jointype to behave similar to "append" but keeps first sheet type and columns (requested by @frosencrantz #1598)
- [zip] add multisave for `.zip` (save each sheet in options.save_filetype format into given .zip file)
- [sysedit] add `sysedit-selected` (bound to `g Ctrl+O`) (requested by @Delapouite #1596)
    - edit cells in multiple rows in `$EDITOR`
    - only handles cell modifications, not added or deleted rows


## Improvements

- [aggregators] add 95 and 99 percentile (p95 and p99)
- [fill-col] speed up `fill-col` for sheets with many empty cells (PR by @midichef #1657)
- [loaders hdf5] add support for arrays of scalars (requested by @linwaytin #1602)
- [graph] fail if no numeric xcols are given
- [open-cell-file] warn when file or url in cell does not exist (requested by @geekscrapy #1540)
- [sqlite] add passthrough options (reported by @cwarden #1622)
- [sqlite] add options.sqlite_onconnect to be executed before running any statement (requested by @cwarden #1622)
- [xml] add passthrough options for xml_parser; default xml_parser_huge_tree=True (PR by @midichef #1668)

## Bugfixes

- [columns] `dup-sheet` now carries over attributes of columns added by `add-column`
- [columns] **SettableColumn** should not be deferred (reported by @frosencrantz #1568)
- [customdate] recognise type-customdate as numeric (requested by @tdussa #1613)
- [describe] fix custom describe aggregators (reported by @edupont #1574)
- [dirsheet] fix incorrect filename with multiple extensions (reported by @kunliugithub #1571)
- [display] show `disp_oddspace` for surrogate escapes (reported by @geekscrapy #1544)
- [graph] fix div-by-zero with only one y-value (reported by @midichef #1673)
- [install] ensure setuptools files have appropriate permissions (reported by @icp1994 #1591)
- [install] update data files in setup.py based on PEP 420 (reported by @Oblomov #1675)
- [keystrokes] add `kDN` and `kUP` to translation table (reported by @djpohly #1336)
- [loaders html] fix loading of relative links in html table (reported by @frosencrantz #1599)
- [loaders xlsx] store `None` as empty string in `save_xlsx` (reported and PR by @dbaynard #1626 #1629)
- [macros] override CLI parsing options for MacrosSheet (reported by @frosencrantz #1607)
- [macros] query again for keystroke if used by existing macro (#1658)
- [macros] do not include `nonLogged` commands in macro (reported by @geekscrapy #1569)
- [macros] add reload for **MacroSheet** (reported by @geekscrapy #1569)
- [menu] 2x ESC should exit menu
- [mouse] fix mouse-clicks on statusbar when splitpane is off (reported by @frosencrantz #1625)
- [numpy] fix loader
- [open_txt] fix Exception with `open-config` when no `~/.visidatarc` (reported by @gunchev #1611)
- [pdb] fix entering of pdb breakpoints for Python 3.9+ (reported by @jasonwirth #1317)
- [sheets] sort all sheets on global **Sheets Sheet** (reported by @franzhuang #1620)
- [types] format int/vlen as true int (reported by @xlucn #1674)
- [unzip-http] fix file extraction (`x`) on remote zip file
- [unzip-http] handle files smaller than 64K (reported by @frosencrantz #1567)
- [zsh-completion] fixed (reported by @pigmonkey #1583; PR by @Freed-Wu #1646)

## API

- raise Exception from causes in utils.py (PR by @cool-RR #1633)
- add `HistogramColumn` to allow overrides (requested by @andycraig #1621)
- easier external numeric types with `@vd.numericType()` decorator (inspired by @s1291 #1394)

- [frequency table] `dive-rows` renamed to `dive-selected`

# v2.10.2 (2022-10-08)

- add .vdx, a simplified new cmdlog format
- add `-N`/`--nothing` command to disable loading .visidatarc and plugin addons
- add `addcol-aggr` to add an aggregator column to the **FreqTable** without needing to
  regenerate it (requested by @geekscrapy #1541)

## Improvements

- [cli] load commandline file arguments from the start (requested by @reagle #1471)
- [cli] `--config=''` now does not try to load any config
- [open] rename `zo` `open-cell` command to `open-cell-file`
- [loaders whl] load python .whl (reported by @frosencrantz #1539)

## Bugfixes

- [cli] fix for empty arg
- [DirSheet] fix bug where `Enter` no longer opened a file from the **DirSheet** (reported by @frosencrantz #1527)
- [input paste] fix pasting via a Path via `Ctrl+Y`  into input (reported by @frosencrantz #1546)
- [menu] allow VisiData to run without menu
- [mouse] catch any curses.getmouse() errors (reported by @geekscrapy #1553)
- [performance] allow vd to be truly idle (reported by WizzardUU #1532)
- [plugins_autoload] catch error for environment having invalid package metadata (reported by @jsdealy #1529)
- [plugins_autoload] catch exception if plugin fails to load
- [plugins-autoload] fix check for if plugins_autoload is set in args
- [plugins-autoload] update for importlib-metadata 5.0 API (reported by @jkerhin #1550)
- [pyobj] undo rename of `open-row`/`open-cell` (were renamed to `open-X-pyobj`) (revert of eff9833e6A)
- [sheets] ensure IndexSheets are precious, and that **SheetsSheet** is not (reported by @frosencrantz #1547)
- [unzip-http] extracting a file now checks for overwrite (reported by @frosencrantz #1452)
- [windows clipboard] fix piping to clip command through stdin (thanks @daviewales for the fix; reported by @pshangov #1431)

## API

- expose `CommandLogBase` (was `_CommandLog`)
- [options] allow FooSheet.options instead of .class_options
- add separate non-async `select_row`, `toggle_row`, and `unselect_row` for selection of single rows
- the before/after decorators now do not fail if api functions they are decorating do not already exist

# v2.10.1 (2022-09-14)

## Improvements

- [docs] document `-d` option (thanks @abitrolly for PR #1515)
- [freq] disable histogram if `disp_histlen` or `disp_histogram` set to 0 or empty string
- [guard] add `guard-sheet-off` which unsets `options.quitguard` on current sheet (thanks @hanfried for PR #1517)
- [menu] add `BUTTON1_CLICKED` (same as `BUTTON1_PRESSED`)
- [open] add `zo` to open file or url from path in current cell

## Bugfixes

- fix Guix build problems (reported by @ryanprior #1499)
- add support for sheet names with multiple `.` (periods) in the name (requested by @geekscrapy #1494)
- [cmdlog] add more portable shebang in vdj
- [date] fix custom date greater than or equal to comparison
- [macros] fix `macro-record` (#1513)
- [macros] refresh `macro-sheet` upon macro addition
- [macros] ensure macros are set upon startup
- [plugins] update usd plugin api (thanks @hanfried for PR #1510)
- [repeat] fix `repeat-` (#1513)
- [status] reduce priority of active colouring (reported by @geekscrapy #804)

## API

- add `ExpandedColumn` to globals
- add `Extensible.before` and `Extensible.after`
  - `def foo` decorated with `@VisiData.before` will run it before `vd.foo()`
  - `def foo` decorated with `@VisiData.after` will run it immediately after



# v2.10 (2022-08-28)

- [plugins] load all entry points in `visidata.plugins` group before config load
  - add entry_points={'visidata.plugins': 'foo=foo'} to plugin load plugin automatically when launching VisiData

- [deps] require `importlib_metadata` >= 3.6
  - following https://github.com/pypa/twine/pull/732

## Improvements

- [draw] redraw only every 100 ms if any keys pending (requested by @ansoncg #1459)
- [IndexSheet] shown name is only final name component
- [loaders html] add table of all links on page (requested by @dufferzafar #1424)
- [loaders html] `open-row` on **LinksSheet** to open url (requested by @dufferzafar #1424)
- [options] add `options.http_req_*` to send headers/etc to requests (requested by @daviewales #1446)
- [options pyobj] add `options.fmt_expand_dict` and `options.fmt_expand_list` for formatting expanded
    list and dict column names (requested by @joe-opensrc #1457)
- [threads-sheet] add `z Ctrl+T` (`threads-sheet`) to open **ThreadsSheet** for current sheet
- [threads-sheet] add `g Ctrl+C` (`cancel-all`) to **ThreadsSheet**
- [zsh] add scripts for zsh completion (PR by @Freed-Wu #1455)
  - tutorial: https://visidata.org/docs/shell/

## Bugfixes

- [addcol-] set cursor at added column for `addcol-new`/`addcol-bulk` (reported by @jsvine #1445)
- [cmdlog] `Ctrl+S` from a **CommandLog** now defaults to `.vdj` (reported by @jsvine #1443)
- [display] format entire string for undetermined width (reported by and fixed by @jsvine #1442)
- [formatter] fix len format strings
- [LastInputsSheet] catch other exceptions during reload
- [loader npz] fix .npz loader (reported by @Shahin-rmz #1440)
- [loader geojson] fix plotting and saving geojson files (fixed by @mwayne #1477)
- [loader geojson] improve feature property manipulation (fixed by @mwayne #1477)
- [menu] upon menu item keypress, move to item (reported by @reagle #1470)
- [menu] fix `ALT+<keystroke>` navigation while within menu (reported by @reagle #1470)
  - now requires two `ESC` to exit
- [open] allow binary files from archives (reported by @frosencrantz #1430)
- [save] do not save unknown filetype as `save_filetype`
- [save visidatarc] only save rows on **OptionsSheet** to visidatarc
- [sheets] fix name reconstruction for files with multiple  and no suffixes (#1450)
- [sheets] do not include empty name parts in sheet name
- [unzip-http] **FreqTableSheet** `open-row` now loads links (reported by @frosencrantz #1458)
- [zip] use correct rowdef in extract (reported by @frosencrantz #1430)
- [zip] do not create directory for extract

## snippets

- add snippets/scrolloff.py which mimics vim's scrollof context lines (requested by @gennaro-tedesco #1441)

## vdplus

- `open-memusage` was moved to vdplus

## API

- add InferColumnsSheet
  - it infers the columns and their types from the rows it gets which are dicts
  - used by json, npy loader
- add vd.printout and vd.printerr for builtins.print to stdout and stderr
- add `vd.view()`
- fix Extensible.init() to work with classes with no `__init__`
- add `Sheet.sidebar` and `Sheet.sidebar_title` properties

## Deprecated

- remove VisiDataSheet
- remove vdmenu

# v2.9.1 (2022-07-21)

- [unzip-http] move urllib3 to optional dependencies

# 2.9 (2022-07-20)

- [ux] add confirming modal dialog
    - only "y" required to confirm
- add XDG support (thanks @jck for the PR #1420)
    - `options.config` default is now `"$XDG_CONFIG_HOME"/visidata/config.py` if `$XDG_CONFIG_HOME` is set and `config.py` exists. If not, falls back to the standard `$HOME/.visidatarc`.
    - vendor [appdirs.py](https://github.com/ActiveState/appdirs/blob/master/appdirs.py)
- [cmdlog] support variables in .vdj (requested by @jungle-boogie #1364)
    - in the .vdj, write variables like so: `${variableName}`
    - then on the CLI: `vd -p foo.vdj variableName=bar`
- [loaders arrow] new Apache Arrow IPC loader/saver (requested by @d-miketa #1369) (requires `pyarrow`)
    - add `.arrow` (file) and .arrows (streaming) formats
    - add more native `parquet` loader via `pyarrow`
- preliminary "windowing" for referencing x rows before and y rows after in an expression (requested by @maxigit #1399, @MMesch #1129, @samuelludwig #1210)
    - press `w` (longname: `addcol-window`) followed by two numbers: the number of rows to aggregate *before* and *after* the current row.
    - there will be a new column, `foo_window`, where each row contains a list of aggregated rows. after that, e.g. `=` `sum(foo_window)` to get a running total for each row
- add `setcol-format-enum` which takes e.g. `A=apple B=banana` and uses that as a mapping when formatting a column.
- vendor `https://github.com/saulpw/unzip-http`; allows the downloading of individual files from a .zip file over http without downloading the entire archive (requires `urllib3` package)
- add `save-source` to save a root sheet directly to its source
- add `setcol-formatter` to specify formatting function used for Column (default: `generic` formats based on type and fmtstr).  Can be `json` or `python` or a custom formatter


## Improvements

- [cli] when `-v` or `-h` VisiData now does not read config or do anything else (requested by @geekscrapy #1340)
- [cmdlog] set `.vdj` to be the default cmdlog format
- [replay] allow column names to be numbers (reported by @frosencrantz #1309)
    - if wishing to reference a column index, required to be an int in a .vdj cmdlog
- [cmdlog] when saving cmdlogs, type column indices as integers, and column names as strings
    - If replaying, and *col* is an `int`, the CmdLog will index by position.
    - If *col* is a `str` it will index by column name.
- [display] preview first n elements of a list/dict cell
- [regex] add unbound `addcol-<regex>` commands
- [rtl] improvements to right-to-left text display (requested by @dotancohen #1392)
- [man] have `vd --help` open the .txt manpage by default (requested by @halloleo #1332)
- [mouse] invert scroll wheel direction (requested by @marcobra #1351)
- [performance] improvements to plotting of empty canvas, multiline display, and draw-ing functions
- [plugins] notify when plugin update available (thanks @geekscrapy for PR #1355)

## Bugfixes

- [aggregators] fail on setting an unknown aggregator in **Columns Sheet** (reported by @geekscrapy #1299)
- [aggregators] handle `delete-cell` case for aggregators column in **Columns Sheet** (reported by @geekscrapy #1299)
- [aggregators] fix quartile aggregators (reported by @pnfnp #1312)
- [aggregators] fix copying of aggregators when duplicating a sheet (reported by @frosencrantz #1373)
- [canvas] do not use "other" label when there are exactly 9 columns being plotted (reported by @tdussa #1198)
- [cli] fix `+:subsheet:col:row:` when `load_lazy` is False
- [delete-row] clear deleted rows from `selectedRows` (reported by @geekscrapy #1284)
- [exec-longname] output warning, if longname does not exist
- [expr] prefer visible columns over hidden columns (reported by @frosencrantz #1360)
- [freeze-sheet] carry over column attributes for freeze-sheet (reported by @frosencrantz #1373)
- [import-python] use command-specific history (reported by @frosencrantz #1243)
- [IndexSheet] fix renaming of sheet names from an IndexSheet (reported by @aborruso #1339)
- [input] handle history for non-string input values (reported by @frosencrantz #1371)
- [loaders pandas] fix (`dup-selected`) `"`of selected rows for **Pandas Sheet** (reported and fixed by @jasonwrith #1315 #1316)
- [loaders usv] swap delimiters (reported by @frosencrantz #1383)
- [loaders usv] save delimiter override options (reported by @frosencrantz #1383)
- [loaders usv] fix saving header with usv row delimiter (reported by @frosencrantz #1383)
- [loaders xlsx] fix clipboard on XlsxSheets (reported by @jungle-boogie #1348)
- [macros] fix macro-record keystroke setting (reported by @fatherofinvention #1280)
- [mouse] stay disabled after input (reported by @holderbp #1401)
- [mouse] fix for pypy3 (thanks @LaPingvino for PR
- [quitguard] refinement of quit-sheet protection (reported by @geekscrapy #1037, #1381)
- [save-selected] get sheet names for saving from selected rows (reported by @aborruso #1339)
- [shell] strip trailing whitespace in `z;` output (reported by @justin2004 #1370)
- [tty] fix bug where piping async output into stdin broke visidata keyboard input (reported by @ovikk13 #1347)
- [undo] fix issue where undoing a reload blanks the current sheet; do not set undos for reload sheet (#1302)
- [unset-option] fix issue where Exception is raised on the next undo-able command run after `unset-option` (reported by @ajkerrigan #1267)
- [windows] require `windows-curses` installation on Windows (thanks @ajkerrigan for PR #1407; reported by @schiltz3 #1268, @aagha #1406)

## API

- add Column.formatter (generic, json, python)
- add SqliteQuerySheet to globals
- `vd.loadConfigFile()` no longer needs a filename argument, and will use `options.config` by default (#211)
- use `newline="` for csv.writer (thanks @daviewales for PR #1368)
- make `ItemColumn` a proper class for inheritance
- add `openJoin` and `openMelt` to allow overriding by plugin sheetsS
- addColumn takes `*cols` (reported by @pyglot #1414)

## Deprecated

- deprecate old vdmenu system
    - remove `Shift+V` command

# 2.8 (2021-12-15)

## Improvements

- [plugins] include pip stderr in warning
- [plugins] use returncode to determine if pip install failed, before adding to imports (thanks @geekscrapy for PR #1215)
- [cmdlog] add sheet creation command to cmdlog (requested by @aborruso #1209)
- [open] strip whitespace from the beginning and end of inputted filenames
- [options] `options.input_history` and `options.cmdlog_histfile` can now be an absolute paths (requested by @geekscrapy #1200)
    - relative paths are relative to `options.visidata_dir`
- [splitwin] automatically switch to pane where sheet is pushed to

## Bugfixes

- [curses] suppress invalid color errors in Python 3.10 (thanks @ajkerrigan for reporting #1227 and for PR #1231)
    - Adapt to [Python 3.10 curses changes](https://docs.python.org/3/whatsnew/3.10.html#curses) which can raise a `ValueError` on invalid color numbers.
- [curses cosmetic] simplify error message, if curses fails to initialise
- [loaders json] skip blank lines in json files, instead of stopping at them (thanks @geekscrapy for PR #1216)
- [loaders jsonl] fix duplicate columns when loading fixed columns sheets in jsonl format (report by @0ceanlight)
    - example of formats with fixed columns is darkdraw's `DrawingSheet`
- [loaders fixed] fix saver (thanks @geekscrapy for PR #1238)
- [loaders postgres] fix recognition of postgres loader (reported by @ryanmjacobs #1229)
- [loaders sqlite] fix the loading of sqlite VIEWs for sqlite version 3.36.0+ (reported by @frosencrantz #1222)
- [help-commands] now lists commands only for the current sheet (reported by @geekscrapy #1217)
- [textcanvas] ENTER on canvas should push copied source sheet for points within cursor
- [pivot freq] use `options.histogram_bins` from source sheet
- [curses cosmetic] fix issue where if a curses initialisation Exception is called, a second Exception follows
- [quit-sheet-free] fix bug where quit-sheet-free, when multiple sheets opened in CLI, was not working (reported by @geekscrapy #1236)
- [options] fix instance where local options sheet was called, instead of global options sheet (thanks @geekscrapy for PR #1241)

## API

- add standard Python `breakpoint()` to drop into the pdb debugger
- export `run()` to global api
- add CsvSheet, ZipSheet, TarSheet to global api (thanks @geekscrapy for PR #1235)

# 2.7.1 (2021-11-15)

- Bugfix: fix Enter on helpmenu (reported by @geekscrapy #1196)

# 2.7 (2021-11-14)

## Improvements

- [movement] bind Home/End to go-top/go-bottom (thanks @geekscrapy #1161)
- [api] add vd.urlcache as alias for urlcache global (thanks @geekscrapy for PR #1164)
- [plugins] do not continue installation if main package fails pip install (thanks @geekscrapy for PR #1194)
- [plugins] allow for plugin records without SHA256; warn if absent (thanks @geekscrapy for PR #1183)
- [load_lazy] do not load subsheets, if `sheet.options.load_lazy` is True (thanks @geekscrapy for PR #1193)
- [save] confirm when `save_foo` function does not exist and saver fallsback to `options.save_filetype` (reported by @geekscrapy #1180)
- [save] `options.save_filetype` default now 'tsv'
- several cosmetic improvements

## Loaders

- [lsv] add `lsv` filetype for simple awk-like records (requested by @fourjay #1179)
- [ods] add `odf` filetype for Open Document Format spreadsheets
- [xlsx] add extra columns (`cellobject`, `fontcolor`, `fillcolor`) if `options.xlsx_meta_columns` (default False) (thanks @hoclun-rigsep for PR #1098)
- [sqlite] allow query/insert (no modify/delete yet) for `WITHOUT ROWID` tables (requested by @stephancb #1111)

## Bugfixes

- [savers compression formats] fix corruption when saving to compression formats (#1159)
- fix "ModuleNotFoundError: no module named 'plugins'" error on startup (#1131 #1152)
- [windows] fix issue with Enter key on Windows (reported by @hossam-houssien #1154)
- [draw] fix multiline rows by making height fixed for all rows (reported by @geekscrapy #916)
- [DirSheet] fix bug where fix key column sheets (e.g. DirSheet, SqliteIndexSheet) keycols were not being saved in batchmode (reported by @geekscrapy #1181)
- [async] make sure all threads started on sheet are cancelable (reported by @geekscrapy #1136)
- [AttrDict] fix bug with setting value on nested AttrDict
- [dup-X-deep] fix error with async_deepcopy (thanks @pstuifzand for fix)
- [join] fix 'inconsistent-keys' issue when joining between XlsxSheet with typed columns and CsvSheet with untyped columns (reported by @davidwales #1124)
- [sqlite] handle sqlite column names with spaces (thanks @davidskeck for PR #1157)
- [sqlite] use `options.encoding` and `options.encoding_errors` for decoding of sqlite db text (reported by @WesleyAC #1156)
- [xlsx] add handling for EmptyCell instances (thanks @hoclun-rigsep for PR #1121)
- [xlsx] gate sheet name cleaning on `options.clean_names` (reported by @davidwales #1122)
- [macos] fix bindings for `Option`+key
- [random-rows] fix import (reported by @geekscrapy #1162)
- [save-selected] better default save filename (reported by @geekscrapy #1180)
- [save] fix bug where saving multiple sheets to a single non-embeddable format did not result in fail (reported by @geekscrapy #1180)
- [slide] fix Shift slide-down and Shift slide-up with arrow keys (reported by @a-y-u-s-h #1137)
- [replay] fix replay where `join-sheets` operation hangs (reported by @agjohnson #1141)
- [undo] no more KeyError when Undoing modifications (reported by @geekscrapy #1133)
- [unfurl-col] fix unfurl-col on cells containing exceptions (reported by @jsvine #1171)

# 2.6.1 (2021-09-28)

## Bugfixes

- [editor] fix sysopen-row (thanks @frosencrantz #1116)
- [loaders fixed] fix saver (#1123)
- [loaders shell] fix copy-files
- [loaders sqlite] fix import error on exception (thanks @jsvine #1125)

# 2.6 (2021-09-19)

## Major feature

- [menu] new hierarchical menu system
    - `Alt+F`, `Alt+E`, etc to open submenus (`Alt+` underlined capital letter in toplevel menu)
    - `Ctrl+H` to activate Help menu (manpage now at `gCtrl+H`)
    - `q` or `Esc` to exit menu
    - Enter to expand submenu item or execute command
    - or left mouse click to activate and navigate menu
    - only show commands available on current sheet
    - sheet-specific commands highlighted with `options.color_menu_spec`
    - new options:
      - `disp_menu`: display menu if inactive (default True).  Can still activate menu with Ctrl+H/Alt+F
      - `disp_menu_keys`: whether to display shortcuts inline (default True)
      - `disp_menu_fmt`: upper right display on menu bar (like `disp_status_fmt`/`disp_rstatus_fmt`)
      - theme colors: `color_menu` `color_menu_active` `color_menu_spec` `color_menu_help`
      - theme chars: `disp_menu_boxchars` `disp_menu_more` `disp_menu_push` `disp_menu_input` `disp_menu_fmt`

## Interface changes

- [expand-col] only expand to one level
- [slide] remove slide row/col with mouse
- [macos] add bindings for Option+key to Alt+key
- [modified] limit use of sheet protection (thanks @geekscrapy #1037)
- [python] rebind g^X to new import-python command (what exec-python was mostly used for)
- [npy] add `npy_allow_pickle` option (default False)
- [join] rename join-sheets on IndexSheet to join-selected; bind both g& and & to join-selected
- [loaders pandas] add error message for unpickling non-dataframes
- [join] fail if no key columns on any sheet (thanks @geekscrapy #1061)
- [loaders xlsx] enable access to cell metadata (thanks @hoclun-rigsep #1088)
- many performance, progress bar, and UI responsiveness improvements

## Bugfixes

- [cli] issue warning if +sheet-position not found (thanks @geekscrapy #1046)
- [clipboard] do not copy newline for syscopy-cell (thanks @geekscrapy #1064)
- [column] detect existing column by row key instead of column name (thanks @geekscrapy #1058)
- [color] set `color_current_row` to the same precedence as `color_current_column` (thanks @frosenrantz #1100)
- [command] do not fail/abort on unknown command
- [draw] Sort indicator on top of More indicator (thanks @geekscrapy #1071)
- [join] fix multiple extend (thanks @cwarden)
- [join] allow extended columns to be modified (thanks @cwarden)
- [join] fix for rowdefs without bool (like pandas)
- [loaders dirsheet] continue after exception in copyfile
- [loaders fixed] fix fixed-format saver
- [loaders fixed] save uses `global options.encoding` (thanks @geekscrapy #1060)
- [loaders mysql] do not stop loading on first error (thanks @SuRaMoN #1085)
- [loaders pandas] fix column rename
- [loaders sqlite] save based on column names, not position
- [loaders sqlite] allow changing value of cells that were NULL (thanks @mattenklicker #1052)
- [loaders sqlite] add message on not currently supporting WITHOUT ROWID (thanks @stephancb #1111)
- [multisave] fix breaking typo
- [open_txt] load new blank sheet for 0 byte files (thanks @geekscrapy #1047)
- [save] do not set a default for `options.save_filetype` (thanks @frosencrantz #1072)
- [split-pane mouse] activate pane on click (thanks @frosencrantz #954)
- [unfurl] handle unfurling exceptions (close #1053)
- [quitguard] confirm quit when set on a specific sheet even if not precious or modified
- [yaml] Fix yaml loader traces on no attribute `_colnames` (thanks @frosencrantz #1104)
- [visidatarc] catch all visidatarc exceptions upon load

# v2.5 (2021-07-08)

- [social] #visidata has moved off of freenode to libera.chat
- [deps] required pandas version for the pandas loader has been bumped to at least 1.0.5
- [caa] new PR submitters required to sign CAA

## Features

- [cli] when no arguments on commandline, open currentDirSheet (previously vdmenu); -f opens empty sheet of that filetype
- [clipboard] bind `x` family to `cut-*` (thanks @geekscrapy #895)
- [date] add specialized comparators for `datetime.date` (thanks @aborruso #975)
    - visidata.date now compares to datetime.date (previously raised exception)
        - identical dates compare equal even if intra-day times are different
        - this does not work for incompletely specified visidata.date; e.g.
            `visidata.date(2016, 10, 29, 4, 0, 0) != visidata.date(2016, 10, 29)`
- [DirSheet] add y/gy to copy file(s) to given directory
- [loaders vds] save non-jsonable cells as string (thanks @pacien #1011)
- [loaders zstd] support loading zstd-compressed files (thanks @lxcode #971)
- [movement] bind `Ctrl+Left/Right` to `go-left`/`right-page` (thanks @davidwales #1002)
- [options] save to foo.visidatarc from OptionsSheet (thanks @njthomas #958)
- [sqlite] RENAME and DROP tables from SqliteIndexSheet
- [unfurl] add `options.unfurl_empty` to include row for empty list/dict (thanks @frosencrantz #898)
- [quitguard] confirm quit/reload only if sheet modified (references #955, #844, #483; thanks @jvns, @frosencrantz)

## Improvements

- [addRow] advance cursor if row inserted before cursor
- [archive] add .lzma as alias for .xz
- [clipboard] gzp pastes None if nothing on clipboard
- [clipboard] make syspaste async
- [clipboard] bind `zP` to syspaste-cells and gzP to syspaste-cells-selected (thanks @jvns and frosencrantz #983, #990)
- [cliptext] better support for combining and variant chars (thanks @lxcode #758 #1034)
- [colors] reduce color swatch size to remove flashing (thanks @frosencrantz #946)
- [encoding] specify encoding explicitly for all Path.open_text (thanks @pacien #1016)
- [error] exceptionCaught(status=False) to add to status history, but not post to status (thanks @frosencrantz #982)
- [freqtbl] copy fmtstr from source col to aggcol (thanks @geekscrapy #1003)
- [help] ENTER/exec-command to execute command on undersheet (thanks @geekscrapy #1011)
- [help] add `all_bindings` hidden column (thanks @frosencrantz #896)
- [inputs] put reused input at end of lastInputs (thanks @geekscrapy #1033)
- [loaders json] streamify save to .json
- [loaders npy] add `npy_allow_pickle` option, default False
- [loaders tsv] increase bufsize to improve loader performance by 10%
- [path] all Path.open track Progress via read/filesize (thanks @jspatz #987)
- [path] add Progress for opening compressed files
- [path] implement line-seek operations (thanks @pacien #1010)
- [regex expand] deprecate `options.expand_col_scanrows`; standardize on `options.default_sample_size` (thanks @jsvine)
- [regex] "match regex" to "capture regex" (thanks @geekscrapy #1032)
- [shell] `addcol-shell` pass command to $SHELL (thanks @juston2004 #1023)
- [shortcut] allow shortcut for jump-sheet to be settable
- [splitwin] push sheet in empty pane iff splitwin
- [stdin] use cli --encoding option for piped data (thanks @pacien #1018)
- [undo] remove undo for reload (replaced with quitguard+confirm)
- [quit] add Shift+Q/quit-sheet-free to quit and free associated memory (thanks @cwarden)

## Display
- [canvas] add `options.disp_canvas_charset` to change displayed chars (thanks @albert-ying #963)
- [canvas] use sheet specific options for draw
- [disp] format list/dict as [n]/{n} only for anytype
- [save] iterdispvals(format=True) convert None to empty string

## Bugfixes

- [batch] ensure quitguard is off during batch mode
- [canvas[ fix error on dive into cursor including y-axis
- [cli] have an actual error if there is a missing argument for final option
- [cli] do nothing (no error) if no sources given
- [clipboard] fix zy/gzp regression (thanks @sfranky #961)
- [clipboard] syscopy-cell do not include column name
- [cmdlog] fix bug where customising replayable options in Options Sheet led to issues opening metasheets (thanks @jsvine #952)
- [cmdlog] fix bug where cmdlog records new sheet name, instead of old sheet name for `rename-sheet` (thanks @aborruso #979)
- [color] track precedence so colorizers apply over `color_current_row`
- [color] determine color availability with `init_pair`
- [color] do not break on nonsense color
- [column] getitemdeep/setitemdeep get/set dotted item key if exists (thanks @frosencrantz #991)
- [column] fix bug where hard crash occurs when cursor on cell of SheetsSheet is on cursorDisplay (thanks @frosencrantz #1029)
- [curses] add default `vd.tstp_signal` for non-cli users
- [execCommand] warn gracefully if bound command longname does not exist
- [expr] setValuesFromExpr do not stop processing on exception
- [join] fix when keys have different names (thanks @aborruso #964)
- [join] fix for rowdefs without bool (like pandas)
- [join] fix multiple extend (thanks @cwarden for reporting)
- [loaders fixed] fix editing in final column for fixed-width load (thanks @mwayne #974)
- [loaders geojson] do not abort plot if rows have errors
- [loaders html] add columns even if not in first row
- [loaders pandas] fix column rename
- [loaders rec json] fix adding new columns for json and rec loaders (thanks @ajkerrigan #959)
- [loaders postgresql] add postgresql scheme (fixes #966) (thanks @zormit #967)
- [loaders sqlite] fix saving deleted cells (thanks @mattenklicker #969)
- [loaders vds] save SettableColumn as Column (thanks @pacien #1012)
- [loaders zip] fix extract-selected-to
- [open] fix regression where opening blank sheets of type tsv, csv, txt, etc was not working
- [plugins] fix stdout/error from plugins installation message (was in bytes, changed to str)
- [quit] remove sheets from **Sheets Sheet** upon quit
- [save-col] fix inputPath error (thanks @savulchik #962)
- [shell] fix `options.dir_hidden`; also apply to dirs when `dir_recurse`
- [textsheet] fix reload after `^O` sysopen

## vdplus

- moved clickhouse, vsh, vgit, windows to vdplus


# v2.4 (2021-04-11)

- [splitwindow] stabilize sheet stack associations
    - `Shift+Z` pushes 'under sheet' (if any) onto other stack
    - `Shift+Z` does not swap panes anymore
    - `g Tab` swaps panes
    - `options.disp_splitwin_pct` is always not sheet-specific

- [status] show nSelectedRows on rstatus

- [color] remove `options.use_default_colors` (thanks @lxcode #939)
    - `options.color_default` can now have both fg and bg
    - other color options which do not specify fg or bg will use the missing component from `color_default`
    - to use terminal default colors, set `options.color_default=""`

## Bugfixes

- [loaders gzip] fix progress bar when opening gzip (thanks @geekscrapy #925)
- [loaders http] fix loading files from url without specifying filetype
- [loaders sqlite] use `TABLE_XINFO` for hidden/virtual columns (thanks @dotcs #945)
- [loaders sqlite] perf improvement: do not pre-count rows (required full table scan)
- [loaders vds] save typed values instead of formatted display values (thanks @frosencrantz #885)
- [loaders xlsx] stringify "header" row values for column names (thanks @davidwales #921)
- [pyobj-show-hidden] grab visibility lvl from sheet specific option (thanks @frosencrantz #947)
- [splitwin] prevent flickering-on-full-window
- [splitwin] if top sheet quit, keep bottom sheet in bottom pane
- [splitwin] full-screen/splitwin close all sheets should be part of the same stack

# v2.3 (2021-04-03)

## Features
    - [colors] allow background colors (thanks @frosencrantz #435)
        - use "*fg* on *bg*" e.g. "212 yellow on 14 red"
            - "bg *bg* fg *fg*" (or reversed)
            - attributes always apply to foreground regardless of position in colorstr
            - as before, only the first valid color in a category (fg/bg) is used; subsequent color names (even unknown ones) are ignored
        - allocate colors on demand, instead of "all" 256 colors as fg
        - **Colors Sheet** now only includes colors actually allocated
    - [colors] set `use_default_colors` default to `True` (was `False`)
    - [delete] do not move deleted values to clipboard (thanks @geekscrapy #895)
        - `delete-*` commands are changed to not alter the clipboard
        - the previous `delete-*` commands are renamed to `cut-*` (unbound)
        - this affects: `delete-row`, `delete-selected`, `delete-cell`, `delete-cells`
    - [jump-first] bound `g^^` to cycle through sheets
    - [null] `zd` / `gzd` `delete-cells` set to `options.null_value` instead of `None`
    - [memories] add MemorySheet on `Alt+M`, `Alt+m` adds current cell to sheet  (thanks @UrDub and @geekscrapy #912)
        - useful for storing values to reference later
        - both names and values can be edited on MemorySheet
        - [aggregator] `memo-aggregator`(z+; formerly called `show-aggregate`) adds value to memory sheet
        - [clipboard] clipboard stored on memory sheet; zy/zp use vd.memory.clipval;
    - [plugins] allow install from github url to local pip repo
    - [plugins] add darkdraw to plugins.jsonl
    - [png] save image as RGBA
    - [pyobj-expr] `Ctrl+X` within `Ctrl+X` input suspends directly into python REPL
    - [splitwin] now involves two different sheetstacks that build and quit independently (thanks @lamchau #894)
        - [splitwin] allows stickier panes for push/quit
    - [splitwin] splitwin-half (`Z`) swaps panes if already active
    - [splitwin] only re-split (with `zZ`) if sheets are not already split, otherwise adjust split percent
    - [save_filetype] if `save_ext` does not exist, or if `options.save_filetype` is different from default, use `options.save_filetype`
    - [vdplus] auto-import, ignore if not available

## Bugfixes
    - [aggregator] fix typo in deciles description (thanks @cwarden #922)
    - [copy] copying BasicRow (new sheets), now does not error (still blank)
    - [cmdlog] for `open-file` source logging in cmdlog, we want paths to physical files, so if src is a **Sheet** grabs its source
    - [defer] fix pasting in deferred sheets
    - [eval] fix **ExprColumns** on empty rows
    - [help] move signal config earlier in runcycle, to accommodate --help (thanks @frosencrantz #926)
    - [open] create blank sheet of appropriate type when path does not exist
    - [pandas] fix conflict between dropped index and existing column (thanks thomanq #937)
    - [plugins] only check for plugins.jsonl once daily (previously: every start-up)
    - [pivot] fix `openRow`
    - [pivot] fix bug with sheet name
    - [png] fix saving directly from canvas
    - [sort] fix sorting of visidata.Path objects (thanks @frosencrantz #897)
    - [splitwin] fix cursor behaviour on both panes when active
        - cursor movement on inactive panes is blocked
    - [SuspendCurses] workaround for bug in curses.wrapper (thanks @frosencrantz #899)
    - [undo] do not set undo for a `commit-sheet`

## Api
    - [addRows] addRows(rows, index, undo) adds rows at index, sets undo if True
        - set undo to False, if using addRows within an addUndo function
    - [deleteBy] add an undo flag to deleteBy
    - [clipboard] change `cliprows` to be a simple list of rows
    - new **DrawablePane** super-base class
    - [json] rowdef now **AttrDict** for massive convenience

# v2.2.1 (2021-02-07)

## Bugfixes
    - [setcol-fill] use row identity to identify selected rows (thanks @frosencrantz, #884)
        - for jsonl, empty rows are identical ({}), and if ones is selected, previously it would result in all of them being filled.
        - also, fill with most recent *non-null* value

## man
    - add a manpage visidata.1
    - fix typo

# v2.2 (2021-01-30)

## Options

    - [cli options] now global by default; use `-n` to set option as sheet-specific instead
        - add `-n`/`--nonglobal` to make subsequent CLI options "sheet-specific" (applying only to paths specified directly on the CLI)
        - keep `-g`/`--global` to make subsequent CLI options "global" (applying to all sheets by default unless overridden)
        - invert the default: now CLI options are global by default (thus `-g` is a no-op unless preceded by `-n` on the CLI)
        - `-g` no longer acts as a toggle

    - [input] add `options.input_history` (thanks @tsibley and @ajkerrigan #468)
        - basename of file to store persistent input history (default of `''` means disabled)
        - caveat: persistent file only read if option given before first input

    - [options.fancy_chooser] now disabled by default--use `Ctrl+X` to open from a choose() prompt

## Types

    - [types] add `floatlocale` type (thanks @Guiriguanche #863)
        - add commands `type-floatlocale` and `type-floatlocale-selected` (unbound by default)
        - `floatlocale` parses based on `LC_NUMERIC` envvar (must be set before launching)
        - parsing is 20x slower than with standard float column
        - will parse commas as decimals (e.g. '1,1') if LC_NUMERIC is set to a locale like 'en_DK.UTF-8'

## Loaders

    - [loaders geojson] add loading and saving support for geojson files (thanks @draco #876)
    - [loaders vds] add loader/saver for custom .vds format (VisiData Sheet) to save column properties and data for multiple sheets in one file
    - [ux] autoload all subsheets by default; set `options.load_lazy` to disable
        - removes a minor friction with unloaded subsheets

    - [loaders http] add `options.http_max_next` to limit api pagination (default 0 - no pagination) (thanks @aborruso #830)

## Bugfixes and Adjustments

    - [cli] fail properly if path cannot be opened
    - [defer] only mention number of deleted rows, if some were deleted
    - [go-pageup go-pagedown] ensure cursor stays in the same relative positions
    - [loaders mysql] fix mysql loader duplicating tables for each database (thanks @SuRaMoN #868)
    - [loaders mysql] perform asynchronous data fetch for mysql loader (thanks @SuRaMoN #869)
    - [loaders pandas] fix empty subsets for dup-selected and frequency table `open-row` (thanks @ajkerrigan #881 #878)
    - [loaders shp] fix display (thanks @dracos #874)
    - [loaders shp] fix saving to geojson (thanks @dracos #876)
    - [replay] fix replaying of .vd with `set-option`
    - [slide] fix bug when sliding key columns to the left, after sliding them to the right
    - [types] add command `type-floatsi-selected` on **Columns Sheet**

    - [expand] errors and nulls can now be expanded with `expand-cols` (thanks @geekscrapy #865)

    - [open] openSource now uses **'global'** `options.filetype` instead of sheet-specific as previous
        - to set the filetype for a file locally, set through cli: `vd -f tsv sample.foo`
        - to set in the **CommandLog**, use sheet="global" with longname="set-option"

    - [loaders http] raise exception if http status is not 20x (thanks @geekscrapy #848)
    - [loaders shp] support more Shapefile types (thanks @dracos #875)

## API
    - add `create` kwarg to `openSource()`, to create the file if it does not exist already
    - [settings] 'global' is now 'default', and 'override' is 'global'
        - 'default' is the default setting within VisiData
        - 'global' is a user override on that default that applies globally
        - sheet-specific overrides global and default, for the sheet it is specific to
        - options set through visidatarc and cli are 'global' unless otherwise specified
    - [save] grab `save_foo` from **SheetType** first
        - allows overrides of sheet-specific saving

# v2.1.1 (2021-01-03)

    - [macros] allow macro interfaces to be longnames (thanks @frosencrantz #787)
    - [save] better default save filename for url sheets (thanks @geekscrapy #824)

## Bugfixes
    - [cmdlog] record column, sheet, and row info for open-cell
    - [cmdlog] catch case of 'override' sheet for set-option
    - [expr-col] `curcol` now works for multiple invocations (thanks @geekscrapy #659)
    - [loaders postgres] account for postgres_schema when rendering Postgres tables (thanks @jdormit for PR #852)
    - [loaders url] fail unknown URL scheme (thanks @geekscrapy for PR #84)
    - [pyobj] fix Pyobj Sheets for lists (thanks @brookskindle #843)
    - [pipe] handle broken pipes gracefully (thanks @robdmc #851)
    - [scroll] fix issue with jagged scrolling down (thanks @uoee #832)
    - [sort] fix bug where total progress in sorting is (100 * # of columns to sort) (thanks @cwarden)

## api
    - format_field formats int(0) and float(0.0) as "0" (thanks @geekscrapy for PR #821)
    - add TypedWrapper.__len__ (thanks @geekscrapy)

# v2.1 (2020-12-06)

    - [add] add bulk rows and cols leave cursor on first added (like add singles)
    - [add] add colname input to `addcol-new`
    - [aggregators] add mode and stdev to aggregator options (thanks @jsvine for PR #754)
    - [api] add options.unset()
    - [columns] add hidden 'keycol' to **ColumnsSheet**  (thanks @geekscrapy for feature request #768)
    - [cli] support running as `python -m visidata` (thanks @abitrolly for PR #785)
    - [cli] add `#!vd -p` as first line of `.vdj` for executable vd script
    - [cli] allow `=` in `.vd` replay parameters
    - [clipboard] clipboard commands now require some selected rows #681
    - [commands] add unset-option command bound to `d` on OptionsSheet #733
    - [config] `--config=''` now ignores visidatarc (thanks @rswgnu for feature request #777)
    - [defer] commit changes, even if no deferred changes
    - [deprecated] add traceback warnings for deprecated calls (thanks @ajkerrigan for PR #724)
    - [display] add sort indication #582
    - [display] show ellipsis on left side with non-zero hoffset (thanks @frosencrantz for feature request #751)
    - [expr] allow column attributes as variables (thanks @frosencrantz for feature request #659)
    - [freq] change `numeric_binning` back to False by default
    - [input] Shift+Arrow within `edit-cell` to move cursor and re-enter edit mode
    - [loaders http] have automatic API pagination (thanks @geekscrapy for feature request #480)
    - [loaders json] improve loading speedup 50% (thanks @lxcode for investigating and pointing this out #765)
        - this makes JSON saving non-deterministic in Python 3.6, as the order of fields output is dependent on the order within the dict
            - (this is the default behaviour for dicts in Python 3.7+)
    - [loaders json] try loading as jsonl before json (inverted)
        - jsonl is a streamable format, so this way it doesn't have to wait for the entire contents to be loaded before failing to parse as json and then trying to parse as jsonl
        - fixes api loading with http so that contents of each response are added as they happen
        - unfurl toplevel lists
        - functionally now jsonl and json are identical
    - [loaders json] try parsing `options.json_indent` as int (thanks @frosencrantz for the bug report #753)
         this means json output can't be indented with a number. this seems like an uncommon use case
    - [loaders json] skip lines starting with `#`
    - [loaders pdf] `options.pdf_tables` to parse tables from pdf with tabular
    - [loaders sqlite] use rowid to update and delete rows
        - note that this will not work with WITHOUT ROWID sqlite tables
    - [loaders xlsx] add active column (thanks @kbd for feature request #726)
    - [loaders zip] add extract-file, extract-selected, extract-file-to, extract-selected-to commands
    - [macros] add improved macro system (thanks @bob-u for feature request #755)
        - `m` (`macro-record`) begins recording macro; `m` prompts for keystroke, and completes recording
        - macro can then be executed every time provided keystroke is used, will override existing keybinding
        - `gm` opens an index of all existing macros, can be directly viewed with `Enter` and then modified with `Ctrl+S`
        - macros will run command on current row, column sheet
        - remove deprecated `z Ctrl+D` older iteration of macro system
    - [regex] use capture names for column names, if available, in `capture-col` (thanks @tsibley for PR #808)
        - allows for pre-determining friendlier column names, saving a renaming step later
    - [save] `g Ctrl+S` is `save-sheets-selected` on **IndexSheet**
        - new command allows some or all sheets on an **IndexSheet** to be saved (and not the sheets on the sheet stack)
    - [saver] add fixed-width saver (uses col.width)
    - [saver sqlite] ensureLoaded when saving sheets to sqlite db
    - [search] `search-next` and `searchr-next` are now bound to n and N (was `next-search` and `search-prev`)
    - [select] differentiate select-equal- and select-exact- (thanks @geekscrapy for feature request #734)
       - previous select-equal- matched type value
       - now select-equal- matches display value
       - add `z,` and `gz,` bindings for select-exact-cell/-row
    - [sheets] sorting on **SheetsSheet** now does not sort **SheetsSheet** itself. (thanks @klartext and @geekscrapy for bug reports #761 #518)
    - [status] use `color_working` for progress indicator (thanks @geekscrapy for feature request #804)
    - [types] add floatsi parser (sponsored feature by @anjakefala #661)
        - floatsi type now parses SI strings (like 2.3M)
        - use `z%` to set column type to floatsi

## Bugfixes

    - [api] expose visidata.view (thanks @alekibango for bug report #732)
    - [color] use `color_column_sep` for sep chars (thanks @geekscrapy for bug report)
    - [defer] frozen columns should not be deferred (thanks @frosencrantz for bug report #786)
    - [dir] fix commit-sheet and delete-row on DirSheet
    - [draw] fix display for off-screen cursor with multiline rows
    - [expr] remove duplicate tabbing suggestions (thanks @geekscrapy for bug report #747)
    - [expr] never include computing column (thanks @geekscrapy for bug report #756)
        - only checks for self-reference; 2+ cycles still raises RecursionException
        - caches are now for each cell, instead of for each row
    - [freeze] freeze-sheet with errors should replace with null
    - [loaders frictionless] assume JSON if no format (thanks scls19fr for bug report #803)
        - from https://specs.frictionlessdata.io/data-resource/#data-location):
            - a consumer of resource object MAY assume if no format or mediatype property is provided that the data is JSON and attempt to process it as such.

    - [loaders hdf5] misc bugfixes to hdf5 dataset loading (thanks @amotl for PR #728)
    - [loaders jsonl] fix copy-rows
    - [loaders pandas] support loading Python objects directly (thanks @ajkerrigan for PR #816 and scls19fr for bug report #798)
    - [loaders pandas] ensure all column names are strings (thanks @ajkerrigan for PR #816 and scls19fr for bug report #800)
    - [loaders pandas] build frequency table using a copy of the source (thanks @ajkerrigan for PR #816 and scls19fr for bug report #802)
    - [loaders sqlite] fix commit-sheet
    - [loaders sqlite] fix commit deletes
    - [loaders xlsx] only reload Workbook sheets to avoid error (thanks @aborruso for bug report #797)
    - [loaders vdj] fix add-row
    - [man] fix warnings with manpage (thanks @jsvine for the bug report #718)
    - [movement] fix scroll-cells (thanks @jsvine for bug report #762)
    - [numeric binning] perform degenerate binning when number of bins greater than number of values
        - (instead of when greater than width of bins)
    -  [numeric binning] if width of bins is 1, fallback to degenerate binning
    - [numeric binning] degenerate binning should resemble non-numeric binning (thanks @setop for bug report #791)
    - [options] fix `confirm_overwrite` in batch mode
        - fix `-y` to set `confirm_overwrite` to False (means, no confirmation necessary for overwrite)
        - make `confirm()` always fail in batch mode
        - make `confirm_overwrite` a sheet-specific option
    - [plugins] only reload **Plugins Sheet** if not already loaded
    - [replay] move to replay context after getting sheet (thanks @rswgnu for bug report #796)
    - [replay] do not push replaying .vd on sheet stack (thanks @rswgnu for bug report #795)
    - [scroll] zj/zk do nothing in single-line mode (thanks @jsvine for suggestion)
    - [shell] empty stdin to avoid hanging process (thanks @frosencrantz for bug report #752)
    - [status] handle missing attributes in `disp_rstatus_fmt` (thanks @geekscrapy for bug report #764)
    - [tabulate] fix savers to save in their own format (thanks @frosencrantz for bug report #723)
    - [typing] fix indefinite hang for typing (thanks @lxcode for issue #794)
    - [windows] add Ctrl+M as alias for Ctrl+J #741 (thanks @bob-u for bug report #741)
    - [windows man] package man/vd.txt as a fallback for when man is not available on os (thanks @bob-u for bug report #745)

## Plugins
- add conll loader to **PluginsSheet** (thanks @polm)
- remove livesearch
- add clickhouse loader

## Commands
- if `options.some_selected_rows` is True, `setcol-expr`, `setcol-iter`, `setcol-subst`, `setcol-subst`, `setcol-subst-all` will return all rows, if none selected

## API
- [columns] add Column.visibleWidth
- [open] additionally search for `open_filetype` within the vd scope
- [select] rename `someSelectedRows` to `onlySelectedRows`
- [select] add new `someSelectedRows` and `options.some_selected_rows` (thanks maufdez for feature request #767)
    - if options is True, and no rows are selected, `someSelectedRows` will return all rows
- [status] allow non-hashable status msgs by deduping based on stringified contents
- [isNumeric] isNumeric is part of vdobj

# v2.0.1 (2020-10-13)

## Bugfixes
    - Fix printing of motd to status

# v2.0 (2020-10-12)

## Additions and Improvements
    - [aggregators] allow custom aggregators in plugins/visidatarc (thanks @geekscrapy for the feature request #651)
    - [loaders xlsx] automatically clean sheet name when saving; warn if sheet name changes (thanks @geekscrapy for the request #594)
    - [columns] unhide height attribute by default (thanks @frosencrantz for feature request #660)
    - add .vcf (VCard) loader
    - [sqlite] remove name of db from an **SqliteSheet**'s name, only tablename
    - [syspaste] make `syspaste-` replayable and undoable (note that `syspaste-` value will be recorded in **CommandLog**)
    - [savers] many text saver filetypes via tabulate library (thanks @jsvine for original vdtabulate plugin)
    - [calc] ExprColumn no longer cached by default
    - [loaders rec] add new .rec file loader and multisheet saver (recutils)
    - [savers] implemented multisheet saver for both json and jsonl
    - [loaders eml] add new .eml file loader for email files with attachments

## Options
    - add `options.incr_base` (thanks @chocolateboy for the suggestion #647)
    - (former) `options.force_valid_colnames` renamed to `options.clean_names`
        - applies to **Sheets** and **Columns** now (thanks @geekscrapy for the request #594)
    - for --X=Y, do not replace - with _ in Y (thanks @forensicdave for bug report #657)
    - add `options.default_height` for visibility toggle (thanks @frosencrantz for feature request #660)
    - add support for `--` option-ending option on CLI.
    - [input] default now `fancy_chooser` = True
        - when fancy_chooser enabled, aggregators and jointype are chosen with a ChoiceSheet.
        - `s` to select, `Enter` to choose current row, `g Enter` to choose selected rows, `q` to not choose any
    - numeric_binning is now True by default (enables numeric binning on **PivotSheet** and **FreqTable** for numeric columns

## Command changes and additions
        - (former) setcol-range (`gz=`) renamed to `setcol-iter`
        - (former) `addcol-range-step` (`i`) renamed to `addcol-incr-step`
        - (former) `setcol-range` (`gi`) renamed to `setcol-incr`
        - (former) `addcol-range-step` (`zi`) renamed to `addcol-incr-step`
        - (former) `setcol-range-step` (`gzi`) renamed to `setcol-incr-step`
        - add `scroll-cells-*` to scroll display of cells while remaining in a Column; bind to [g]z{hjkl}
        - (former) unbind z{hjkl} from `scroll-col` (thanks @geekscrapy for feature request #662)
        - add `type-floatsi` bound to `z%` (#661)
        - `reload-selected` now reloads all **Sheets** if none selected (thanks @geekscrapy for PR #685)
        - add customdate with fixed fmtstr for parsing (use `z@` and input a fmtstr compatible with strptime (thanks @suntzuisafterU for feature request #677)

## Bugfixes
    - [DirSheet] use changed ext as filetype when loading files (thanks @frosencrantz for bug report #645)
    - [slide] several major improvements to column sliding; key column sliding now works (thanks much to @geekscrapy for bug hunting #640)
    - [open-row] **Sheets Sheet** should be removed from stack upon `open-row` (thanks @cwarden for the bug report)
    - [cli] re-add --version (thanks @mlawren for bug report #674)
    - [open-config] fix `gO` (thanks @geekscrapy for bug report #676)
    - [splitwin] handle swap case for single sheet (thanks @geekscrapy for bug report #679)
    - [loaders xlsx] handle `None` column names for all **Sequence Sheet** loaders (thanks @jsvine for bug report #680)
    - [settings] retrieve from cache for top sheet if obj is None (thanks @aborruso for the bug report #675)
    - [settings] check if option is set on specific sheet before falling back to override
    - [describe] have **DescribeSheet** use source column's sheet's `options.null_value` to calculate its null column (thanks @aborruso for the bug report #675)
    - [undo] ensure that undos for complex commands (like `expand-cols`) are set more frequently (thanks @frosencrantz for the bug report #668)
        - it is still possible to find race conditions if the user presses commands fast enough, however they should happen far less frequently
    - [vlen] fix numeric binning for `vlen()` (thanks @frosencrantz for bug report #690)
    - [pivot] fix pivot case where no aggregator is set
    - [pyobj] fix filtering for **PyobjSheet**
    - [DirSheet] fix sorting for directory column of **DirSheet** (thanks @frosencrantz for bug report #691)
    - [json] fix bug saving cells with nested date values (thanks @ajkerrigan for PR #709)
    - [input] fix Ctrl+W bug when erasing word at beginning of line
    - [plugins] import `.visidata/plugins` by default
    - [pandas] use a safer `reset_index()` to avoid losing data when updating a pandas index (thanks @ajkerrigan for PR #710)
    - [threads] disable `add-row` on **ThreadsSheet** (thanks @geekscrapy for bug report #713)

## deprecated
    - complete removal of `status` and `statuses` from deprecated (thanks @frosencrantz for bug report #621)
        - longnames are now `open-status` and `show-status`
    - remove `cursorColIndex`

## API and Interface
    - `Sheet(*names, **kwargs)` autojoins list of name parts
    - `openSource()`, `aggregator()`, and `aggregators` are now part of vdobj
    - `set_option` is now `setOption`
    - move `isError` to `Column.isError`
    - deprecate `load_pyobj`, now **PyobjSheet**
    - add `.getall('foo_')` which returns all options prefixed with `'foo_'`; deprecated `options('foo_')`
    - `nSelected` is now `nSelectedRows`
    - make `Column.width` property, so setting is same as `Column.setWidth`
    - `evalexpr` is now `evalExpr`
    - `format` is now `formatValue`
    - `SettableColumn.cache` is now `._store`
    - `vdtype()` is now `vd.addType()`
    - add `addColumnAtCursor` (thanks @geekscrapy for bug report #714)

## Plugins
    - update sparkline (thanks @layertwo #696)
    - plugin dependencies now install into `plugins-deps` (former plugin-deps)

## Dev niceties
    - Fully automate dev setup with Gitpod.io (thanks @ajkerrigan for PR #673)


# v2.-4 (2020-07-27)

## Additions and Improvements
    - [cmdlog] allow sheet-specific set-option for replay
    - [columns] add default uppercase names for created columns (like VisiCalc)
        - these names are global; no default name is ever reused
    - [cosmetic] a column with a width 1 will now display (thanks @frosencrantz for the bug report #512)
    - [defer] move defermods and vls back into vdcore
        - configure sqlite and DirSheet to use it
    - [dir] allow explicit filetype when loading a directory (thanks @geekscrapy for the bug report #546)
    - [errors] ErrorsSheet on `g Ctrl+E` lists errors, instead of concatenating
    - [expand-cols] account for all visible rows when expanding a column (thanks @ajkerrigan for PR #497)
    - [loaders csv] use `options.safe_error` for cell exceptions on save
    - [loaders http] use file format in path if loader available (thanks @jsvine for PR #576 and bug report #531)
        - if not, fail back to MIME type (prev behaviour)
    - [loaders imap] add loader for imap://
    - [loaders json] handle non-dict rows in json data (thanks @ajkerrigan for PR #541 and @jsvine for bug report #529)
    - [loaders jsonl] show parse errors in every column
    - [loaders MySQL] add support for MySQL loader (thanks @p3k for PR #617)
    - [loaders pandas] upgrade pandas to 1.0.3 (thanks @ajkerrigan for PR #563)
    - [loaders pandas] add auto-loaders for feather, gbq, orc, parquet, pickle, sas, stata (thanks @khughitt for bug report #460)
    - [loaders pdf] add simple pdf loader
    - [loaders postgres] add support for connecting directly to rds (thanks @danielcynerio for PR #536)
        - the url has the following format: `rds://db_user@hostname:port/region/dbname`
        - it assumes that the AWS IAM for the user is configured properly
    - [loaders xls/xlsx] add save_xls and save_xlsx (thanks @geekscrapy for PR #574)
    - [loaders yaml] allow diving into YAML rows (thanks @ajkerrigan for PR #533)
    - [loaders yaml] use the default safe YAML loader (thanks @tsibley for the PR #600 )
        - the full loader is unsafe because serialized files can be constructed which run arbitrary code during their deserialization
        - the safe loader supports a very large subset of YAML and supports the most common uses of YAML
    - [loaders yaml] support files containing multiple documents (thanks @tsibley for PR #601)
    - [options] set visidata_dir and config from `$VD_DIR` and `$VD_CONFIG` (thanks @tsibley for bug report #448)
    - [type fmtstr] thousands separator (thanks @dimonf for bug report #575)
        - default for int/float is string.format for roundtripping accurately in data text files like csv
        - if fmtstr starts with '%', use locale.format_string (with grouping)
        - otherwise, use python string.format
        - currency uses locale, and is grouped.
    - [quitguard] if set on specific sheet, only confirm quit on that sheet (thanks @jsvine for bug report #538)
    - [undo] add undo for `rename-col-x` family, mouse slide, and `reload-sheet` (thanks @jsvine for feature request #528)

## Command changes
    - [add-sheet] renamed to open-new; new sheet always has a single column
    - [config] bind `g Shift+O` back to open-config (#553)
    - [dive] convert many dive- commands to open- (#557)
        - add open-row bound to `ENTER` on Sheet itself
        - add `open-source` unbound on BaseSheet
        - deprecate `dive-*` longname
    - [options] options-global now `Shift+O`; options-sheet now `z Shift+O`
    - [multi-line] have visibility toggle Multi-Line Row on TextSheets (Closes #513)
        - used to toggle `wrap`

## Command additions
    - [canvas] add resize-x/y-input commands to set x/y axis dimensions (thanks @pigmonkey for feature request #403)
    - [errors] add select-error-col and select-error (thanks @pigmonkey for feature request #402)
    - [input] `Ctrl+Y` paste from cell clipboard
    - [input] Ctrl+Left/Right move cursor to prev/next word
    - [iota] add `i` family of commands (iota/increment)
        - (former) setcol-range (`gz=`) renamed to `setcol-iter`
        - `addcol-range-step` (`i`): add column with incremental value
        - `secol-range` (`gi`): set current column for selected rows to incremental values
        - `addcol-range-step` (`zi`): add column with incremental values times given step
        - `setcol-range` (`gzi`): set current column for selected rows to incremental values times given step
    - [mouse] add unbound `mouse-enable` and `mouse-disable` commands
    - [quitguard add unbound `guard-sheet` command to set quitguard on current sheet (thanks jsvine for feature request #538)
    - [unfurl-col] add command, bound to `zM`, which does row-wise expansion of iterables in a column (thanks @frosencrantz for feature request and jsvine for initial code sample #623)
        - thanks @jsvine for name and initial implementation

## Options
    - [cli] custom cli option parsing (thanks @tsibley for the behaviour request #573)
        - `--options` apply as sheet-specific option overrides to the sources following them
        - the last setting for a given option is the cli-given override setting (applies to all cli sources, unless they have the option already set)
        - this allows both
            - `vd -f csv foo.txt`
            - `vd foo.txt -f csv`
        - `--help` opens the manpage
        - `-g` prefix sets option globally for all sheets
    - [cli] add --imports (default "plugins") (thanks @tsibley for feature request #448)
        - space-separated list of modules to import into globals before loading .visidatarc
        - plugins can be installed by VisiData without modifying .visidatarc
    - [chooser] experimental `options.fancy_chooser`
        - when fancy_chooser enabled, aggregators and jointype are chosen with a ChoiceSheet.
        - press `ENTER` on any row to choose a single option, or select some rows, and press `ENTER` to choose the selectedrows
        - warning: the mechanism to do this effectively launches another instance of visidata, and so it is possible to get into an embedded state (if you jump around sheets, for example, instead of selecting). 'gq' should still work (thought `CTRL+Q` may need to be pressed several times).
    - [dir] add `-r` alias for `--dir-recurse`
    - [join-cols] add `options.value_joiner` to combine cell values for join-col (thanks @aborruso for feature request #550)
    - [join-cols] add `options.name_joiner` to combine column names for join-col, and sheet names for dive-row (thanks @aborruso for feature request #550)
        - sheet names for join-sheets are still joined with '+' or '&' for the time being
    - [loaders html] add `options.html_title` to exclude the sheetname when saving sheet as html table (thanks @geekscrapy for PR #566)
    - [loaders postgres] add support for custom postgres schema (Thanks @p3k for PR #615)
        - schema defaults to `public` but can be overridden using the `--postgres-schema` flag:
        - `vd --postgres-schema=foo postgres://user:pw@localhost/foobar`
    - [loaders zip] -f filetype now applies to inner files
    - [mouse] add options.mouse_interval to control the max time between press/release for click (ms)
        - set to 0 to disable completely
    - [pyobj] add `options.expand_col_scanrows` to set the number of rows to check when expanding columns (0 = all)
    - [type fmtstr] add fmtstr options for numerical types
        - add options.disp_currency_fmt
        - add options.disp_int_fmt
        - add options.disp_date_fmt

## Plugins
    - [dependencies] install plugin dependencies into vd dir (thanks @tsibley for feature request #448)
    - [diff] diff is now a plugin
        - `--diff` is not available as a cmdline argument anymore
    - [vds3] bumped to 0.4 (@ajkerrigan)
    - [marks] initial release 0.1; marks selected rows with a keystroke; utils for selecting + viewing marked rows (@saulpw)
    - [genericSQL] initial release (1.0); basic loader for MySQL (Oracle, MySQL) (@aswanson)
    - [diff] is now a plugin (@saulpw)

## Bugfixes
    - [cmdlog] fix case where CommandLog `open-` entries would not be replayable
    - [cmdlog] record keystrokes for command
    - [cmdlog] global cmdlog behaviour is now consistent with VisiData v1.5.2 cmdlog
    - [dirsheet] check if directory before grabbing filetype from ext (thanks @frosencrantz for bug report #629)
        - handles case where `.` in directory name
    - [helpsheet] do not include deprecated longnames (thanks @frosencrantz for bug report #621)
    - [input] flush input buffer upon newline in input; prevent pastes with accidental newlines from becoming keystrokes (thanks @geekscrapy for bug report #585)
    - [loaders csv] PEP 479 fix for csv loader (thanks @ajkerrigan for PR #499)
        - This avoids the following error when opening CSV files in Python 3.8: `RuntimeError: generator raised StopIteration`, but maintains the behaviour of gracefully handling malformed CSV files.
        - References:
            - https://www.python.org/dev/peps/pep-0479/#examples-of-breakage
            - https://github.com/python/cpython/pull/6381/files
    - [loaders html] cast to str before writing (thanks @geekscrapy for bug report #501)
    - [loaders html md] preserve formatting of display values when saving
    - [loaders html] fix string formatting issue for the html table name when saving (thanks @geekscrapy for PR #566)
    - [loaders pandas] bugfixes for sort (thanks @ajkerrigan for PR #496)
    - [loaders pandas] fix row deletion + its undo (thanks @ajkerrigan for PR #496)
    - [loaders pandas] improve regex select/unselect logic (thanks @ajkerrigan for PR #496)
    - [loaders pandas] fix row selection/deselection (thanks @ajkerrigan for PR #496)
    - [loaders postgres] load an estimate of row numbers for improved performance (thanks @danielcynerio for PR #549)
    - [loaders postgres] fix expand column to work on a json column in postgres (thanks @danielcynerio for PR #552)
    - [loaders sqlite] save display value if not supported sqlite type (thanks @jtf621 for bug report #570)
    - [loaders xml] correctly copy columns; fix path (#504)
    - [numeric-binning] fix numeric-binning bug with currency type column
    - [dir] fix dup-rows-deep on DirSheet (thanks @geekscrapy for bug report #489)
    - [rstatus] fix rstatus when repeating a command with no keystrokes (Thanks @ajkerrigan for bug report #577)
    - [save-sheets] fix saving multi-sheets as individual files to directory
    - [settings] remove internal option defaults from cmdlog
    - [sheets_all] make opened .vd/.vdj precious
    - [transpose] handle case where columns are numeric (thanks @frosencrantz for bug report #631)
    - [undo] fix undo with duplicate-named sheets (thanks @jsvine for bug report #527)
    - [utils] Fix namedlist bug with column named after VisiData attrs (particularly 'length') (thanks @tsibley for bug report #543)
 

## Infrastructure / API
    - [asyncsingle] ensure that unfinished threads decorated with @asyncsingle do not block upon sync()
        - used so that domotd() and PluginsSheet().reload() do not block replay progression
    - [open-] switch from vd.filetype to open_ext; deprecate vd.filetype
    - [warnings] output Python warnings to status

# v2.-3 (2020-03-09)

## Major changes
    - [cosmetic] change default column separators
    - [json] make json load/save key order same as column order (ensures round-trip #429)
    - [commands.tsv] remove commands.tsv; move helpstr into code

## Major features
    - add Split Window
        - options.disp_splitwin_pct (default: 0) controls height of second sheet on screen
    - add .vdj for cmdlog in jsonl format
    - add plugins/bazaar.jsonl for PluginsSheet in jsonl format

### new commands
    - `splitwin-half` (`Shift+Z`)    -- split screen, show sheet under top sheet
    - `splitwin-close` (`g Shift+Z`) -- closes split screen, current sheet full screens
    - `splitwin-swap` (`TAB`)        -- swap to other pane
    - `splitwin-input` (`z Shift+Z`) -- queries for height of split window
    - `repeat-last`  (unbound)       -- run the previous cmd longname with any previous input (thanks #visidata for feature request! #441)
    - `repeat-input` (unbound)       -- run the last command longname with empty, queried input (thanks #visidata for feature request! #441)
    - `resize-cols-input` (`gz_`)    -- resize all visible columns to given input width
        - thanks @sfranky for feature request #414
    - `save-col-keys` (unbound)      -- save current column and key columns
        - fixes #415; thanks @sfranky for feature request

### new options
    - options.disp_float_fmt; default fmtstr to format for float values (default: %0.2f)
        - thanks khughitt for PR! #410

## Additions and Improvements
    - add merge jointype (thanks @sfranky for feature request #405)
        - like "outer" join, except combines columns by name and each cell returns the first non-null/non-error value
        - use color_diff to merge join diffs
        - on edit, set values on *all* sheets which have the given row
    - adjust `save-cmdlog` input message for clarity
    - all sheets have a name (thanks @ajkerrigan for helping iron out the kinks with PR #472)
    - add args re-parsing to handle plugin options (helps with #443; thanks tkossak for bug report)
    - vdmenu should only get pushed outside of replay and batch mode
    - move cursor to row/col of undone command (thanks @jsvine for request)
    - move urlcache into async reload (affects PluginsSheet and motd)
    - add 'type' column to `SheetsSheet`

### Command changes

- `HOME`/`END` now bound to `go-leftmost`/`go-rightmost`
    - thanks [@gerard_sanroma](https://twitter.com/gerard_sanroma/status/1222128370567327746) for request
- `z Ctrl+HOME`/`z Ctrl+END` now bound to `go-top`/`go-bottom`
- `Ctrl+N` now bound to `replay-advance`

### longname renamings
- `search-next` (was `next-search`)
- `search-prev` (was `prev-search`)
- `jump-prev` (was `prev-sheet`)
- `go-prev-value` (was `prev-value`)
- `go-next-value` (was `next-value`)
- `go-prev-selected` (was `prev-selected`)
- `go-next-selected` (was `next-selected`)
- `go-prev-null` (was `prev-null`)
- `go-next-null` (was `next-null`)
- `go-right-page` (was `page-right`)
- `go-left-page` (was `page-left`)

## Plugins
    - add usd plugin
        - provide USD(s) function to convert strings with currencies to equivalent US$ as float
        - uses data from fixer.io
    - add vds3 by @ajkerrigan
        - initial support for browsing S3 paths and read-only access to object
    - add "provides" column for plugins (helps with #449; thanks @tsibley for feature request)
    - standardize author in bazaar.jsonl
        - "Firstname Lastname @githbhandle"

## Bugfixes
    - [cmdlog] fix issue with `append_tsv_row`, that occurred with `options.cmdlog_histfile` set
    - [replay] fix replaying of rowkeys
    - [replay] fix race condition which required the `--replay-wait` workaround
    - [plugins] ensure that `options.confirm_overwrite` applies to plugin installation
    - [slide] fix slide-leftmost
        - had inconsistent behaviour when a sheet had key columns
    - [slide] use visibleCol variants, such that slide works as expected with hidden cols
    - [options min_memory_mb] disable (set to 0) if "free" command not available
    - [core] auto-add raw default column only if options.debug (fixes #424; thanks @frosencrantz for bug report)
    - [cli] fix --config (thanks @osunderdog for bug report! #427)
    - [draw] fix status flickering that occurred with certain terminals (thanks @vapniks for bug report #412)
    - [txt save] save all visibleCols instead of only first one
    - [json] avoid adding columns twice when loading JSON dicts (thanks @ajkerrigan for bug report (#444) and PR (#447)
    - [fixed] fixed error that occurs when there are no headerlines (thanks @frosencrantz for bug report #439)
    - [pcap] update loader with modern api
    - [csv] catch rows with csv.Errors and yield error msg
    - [curses] keypad(1) needs to be set on all newwin (fixes #458)
    - [save-sheets] address two bugs with `g Ctrl+S`
    - [batch api] override editline() in batch mode (addresses #464; thanks @Geoffrey42 for bug report)
    - [replay] better handling of failed confirm (addresses #464; thanks @Geoffrey42 for bug report)
    - [asyncthread] with changed decorators, asyncthread should be the closest decorator to the function
        - if it is not, the act of decorating becomes spawned off, instead of calls to the function being decorated
    - [canvas] update Canvas delete- commands with current API (fixes #334)

## Infrastructure / API
    - rename `Sheet` to `TableSheet`
        - deprecate `Sheet` but keep it around as a synonym probably forever
    - use HTTPS protocol for git submodules (thanks @tombh for PR #419)
        - this allows installation of VisiData in automated environments such as
        Dockerfiles where the git user is not logged into Github
    - unit tests have been migrated to pytest
    - use counter to keep track of frequency of column names
        - for joins, we want un-ambiguous sheets of origin when more than one sheet has a c.name
    - all sheets use addColumn api instead of manually appending columns
    - set terminal height/width via LINES/COLUMNS via curses.use_env (thanks halloleo for feature request #372)
    - update pip command to pull development branch of vsh (thanks @khughitt for PR #457)
    - change longnames *-replay to replay-*
    - rename vd.run() to vd.mainloop()
    - `vd.save_foo(p, *sheets)` throughout
    - standardize on vd.exceptionCaught
    - Sheet.addRows renamed to Sheet.addNewRows
    - option overrides can be done with SubSheet.options
    - options set with Sheet.options
    - extend status() varargs to error/fail/warning
    - add @BaseSheet.command decorator
    - rename tidydata.py to melt.py
    - deprecate globalCommand; use BaseSheet.addCommand
    - remove vd.addCommand
    - deprecate theme(); use option() instead
    - deprecate global bindkey/unbindkey
    - move commands, bindkeys, `_options` globals to vd object
    - DisplayWrapper compares with its value
        - this allows sensible colorizers like `lambda s,c,r,v: v==3`
    - Sheet.addColorizer now apply to single sheet itself (fixes #433; thanks @frosencrantz for bug report)
    - add Sheet.removeColorizer (thanks @frosencrantz for feature request #434)

# v2.-2 (2019-12-03)

## Major changes
    - [cmdlog] every sheet now has its own cmdlog
        - change `Shift+D` to `cmdlog-sheet`, with commands from source sheets recursively
        - `gShift+D` now `cmdlog-all`
        - `zShift+D` `cmdlog-sheet-only`
    - [dirsheet] VisiData's DirSheet is readonly; move write-mode for DirSheet to `vls` (see plugins)
    - [options] `options-global` bound to `gO`and `options-sheet` to `O`
        - `open-config` is now unbound (previously `gO`)
    - [defermods] has been moved to an opt-in plugin
    - [vdmenu] launching `vd` without a source file, opens menu of core sheets
        - press `Enter` to open sheet described in current row

## Major Features
    - [IndexSheet] index into sub-sheets from command line (thanks @aborruso for suggestion #214)
        - currently works for html and hdf5 loaders
        - `+:subsheet:col:row` in cli 
            - `subsheet` the topsheet upon load, with cursor located in cell at `row` and `col`
        - `+:subsheet::` to ignore row/col
        - can name toplevel source index if more than one: `+toplevel:subsheet::`

## Additions and improvements
    - [add-rows] now undo-able
    - [aggregators] show-aggregate with quantiles (thanks @wesleyac for feature request #395)
    - [cli] `-P <longname>` on commandline executes command <longname> on startup 
    - [cmdlog] jump commands are not logged
    - [config] set VisiData height/width via LINES/COLUMNS envvars (thanks @halloleo for suggestion #372)
    - [csv] add `csv_lineterminator` option (default: '\r\n') (thanks @dbandstra for bug report #387)
        - retain csv writer default DOS line endings
    - [describe] add `options.describe_aggrs` (thanks @unhammer for suggestion #273)
        - space-separated list of statistics to calculate for numeric columns
        - default to existing 'mean stdev'
        - add this to .visidatarc for e.g. a harmonic mean to be added automatically to the describe sheet:
            ```
            from statistics import harmonic_mean
            options.describe_aggrs += ' harmonic_mean' # note the leading space
            ```
    - [describe] add hidden "type" col (thanks aborruso for suggestion #356)
    - [dirsheet] add `open-dir-current` command to open the DirSheet for the current directory
    - [help] add `help-commands-all` on `gz^H` (thanks @frosencrantz for suggestion #393)
    - [help] add `help-search` command (thanks @paulklemm for suggestion #247)
        - opens a commands sheet filtered by the input regex.
    - [loaders] add --header and --skip universal handling to all sheets that inherit from `SequenceSheet` (currently tsv/csv/fixed/xlsx/xls)
    - [menu] if no arguments, open VisiData Main Menu instead of DirSheet
    - [plugins] update PluginsSheet to add sha256 and vdpluginsdeps 
    - [plugins] PluginsSheet now loads plugins in `~/.visidata/plugins/__init__.py` instead of in `~/.visidatarc`
        - to use this feature, add `from plugins import *` to `~/.visidatarc`
    - [pyobj] for security reasons, `.py` loader moved out of VisiData core and into snippets
        - Note that the PyObj loader auto-imports `.py` modules upon loading them
    - [ttf] use `Enter` to plot instead of `.`

## Plugins
    - add hello world minimal plugin
    - update viewtsv example (thanks @suhrig for --skip improvement suggestions #347)
    - add vmailcap with `^V` to view by mimetype (thanks @cwarden for suggestion)
    - add basic frictionless loader (thanks @aborruso for suggestion #237)
        - `-f frictionless` with .json either http[s] or local file
        - .zip may not work yet
    - add fdir filetype; opens a DirSheet for a .txt with a list of paths
    - move trackmod and defermod out of VisiData core and into module defermods.py
        - defermods defers saving to source until commit-sheet
            - deleted rows are colored red and visible until commit
        - trackmods tracks changes in source sheet until save-sheet
            - deletes are removed upon delete-row(s)
        - defermods and trackmods are not on by default, `import visidata.defermods` must be added to visidatarc
        - plugin/loader authors: by default, all sheets that inherit from BaseSheet have .defermods=False and .trackmods set to True when defermods is imported
    - create package `vsh`; add to it `vls`, `vping`, `vtop`
        - `vls` contains write-mode for DirSheet
    - add vmutagen for audio tags on DirSheet
        - `Alt+m` to add the mutagen columns on the DirSheet
    - add geocoding using nettoolkit.com API
        - add `addcol-geocode` command to add lat/long columns from location/address column
    - new commands in rownum plugin
        - `addcol-rownum` adds column with original row ordering
        - `addcol-delta` adds column with delta of current column
    - vtask is now a discrete plugin

## Bugfixes
    - [bindkey] move global bindkey after BaseSheet bindkey (thanks @sfranky for bug report #379)
    - [cmdlog] now will check for `confirm-overwrite`
    - [dirsheet] commit/restat/filesize interactions (thanks @Mikee-3000 for bug report #340)
    - [dirsheet] pass filetype to openSource
        - if filetype is not passed, options.filetype would overload file ext
    - [expr] catch recursive expression columns (columns that calculate their cells using themselves) (thanks @chocolateboy for bug report #350)
    - [fixed] various improvements to fixed-width sheet loader (thanks @frosencrantz for thorough bughunting #331)
    - [http] use options.encoding when no encoding is provided by responses headers (thanks @tsibley for the PR #370)
    - [join] joining columns in the ColumnSheet resulted in exception (thanks @frosencrantz for bug report #336)
    - [load] fix replay sync bug (required wait previously)
        - however, look out for `vd *` with lots of big datasets, they will now all load simultaneously
    - [longname] fix getCommand() error reporting
    - [mbtiles] now works again
    - [metasheets] created VisiDataMetaSheet which sets system TsvSheet options
        - now changes in tsv options for source files will not affect HelpSheet, CmdLog or PluginsSheet
        - thanks frosencrantz for bug report #323
    - [options] no error on unset if option not already set
    - [path] filesize of url is 0
    - [path] fix piping bug (vd failed to read stdin) (thanks @ajkerrigan for bug report #354)
    - [plugins] ensure consistent Python exe for plugin installs (thanks @ajkerrigan for fix)
    - [plugins] make plugin removal more predictable (thanks @ajkerrigan for fix)
    - [prev-sheet] would stack trace if more than one sheet loaded and no other sheet visited (thanks @frosencrantz for bug report #342)
    - [regex] will not silently fail if some example rows are not matches
    - [save] convert savers to use itervalues
        - itervalues(format=False) now yields OrderedDict of col -> value
            - value is typed value if format=False, display string if True
        - options.safety_first will convert newlines and tabs to options.tsv_safe_newline and options.tsv_safe_tab (thanks @mesibov for bug report #76)
    - [sheets] colorizer exceptions are now caught
    - [sheets] keycols now keep order they are keyed
    - [sysedit] trim all trailing newlines from external edits (thanks @sfranky for bug report #378)
    - [tsv] column name "length" prevented loading (thanks  @suhrig for bug report #344)
    - [undo] redo with cmd on first row did not move cursor (thanks @Mikee-3000 for bug report #339)
        - now row/col context are set as strings, even when they are numeric indices

## Infrastructure / API
    - [add-row] create a default newRow for Sheet (thanks @for-coursera for bug report #363)
    - [calc] add INPROGRESS sentinel
        - sentinel that looks like an exception for calcs that have not completed yet
    - [extensible] add new cached_property, which caches until clear_all_caches, which clears all cached_property
    - [Fanout] add Fanout
        - fan out attribute changes to every element in a list; tracks undo for list
    - [lazy_property] newSheet and cmdlog are now lazy_property
        - this enables the overwriting and extending of them by plugins
    - [loaders] add sheet.iterload()
        - will use sheet.source to populate and then yield each row
    - [loaders] vd.filetype(ext, ExtSheet) to register a constructor
    - [loaders] add Sheet.iterrows() to yield row objects 
        - grouping use iterrows() for streaming input
        - __iter__() yields LazyComputeRows
            ```
            for row in vd.openSource('foo.csv'):
                print(row.date, row.name)
            ```
    - [IndexSheet] refactor SheetsSheet parent to IndexSheet
        - HtmlTablesSheet now inherits from IndexSheet
        - excel index changed to standard IndexSheet model
        - VisiDataSheet changed into IndexSheet
        - move join-sheets to IndexSheet
    - [options] add unset() to unset options (thanks @khughitt for suggestion #343)
    - [path] consolidate PathFd, UrlPath, and HttpPath into Path
    - [SequenceSheet] refactor tsv, csv, xls(x), fixed_width to inherit from SequenceSheet
    - [sheets] vd.sheetsSheet is sheetstack, vd.allSheetsSheet is sheetpile
    - [sheets] rename LazyMap to LazyChainMap and LazyMapRow to LazyComputeRow
    - [shortcut] BaseSheet.shortcut now property
    - [status] make right status more configurable (thanks @layertwo #375 and khugitt #343 for filing issues)
        - BaseSheet.progressPct now returns string instead of int
        - BaseSheet.rightStatus() now returns string only (not color)
        - by default uses `options.disp_rstatus_fmt`, configured like `disp_status_fmt`
        - progress indicator (% and gerund) moved out of rightStatus and into drawRightStatus
    - [undo] use undofuncs to associate command with its undo
    - [undo/redo] moved to undo.py
    - [vd] add sheet properties for errors and statuses
    - [vd] vd.quit() now takes `*sheets`
    - [vd] rename main() to main_vd()

# v2.-1 (2019-08-18)

## Major changes

    - Minimum Python requirement bumped to 3.6
    - Several interface changes (see below)

## Major features

    - add Alt/Esc as prefix; Alt+# to go to that sheet
       - Alt+ layer not otherwise used; bind with `^[x` for Alt+X
    - undo/redo
        - [new command] `options.undo` (default: True) to enable infinite linear undo/redo
        - provisionally bound to `Shift+U` and `Shift+R`
        - will undo most recent modification on current sheet
        - `undoEditCells` assumes commands modified only selectedRows
    - multi-line rows
        - toggle by pressing `v` on any sheets with truncated values
    - range binning for numeric columns
        - `options.numeric_binning` (default: False) is the feature flag
        - [feature freqtbl] numeric binning for frequency/pivot table
        - `options.histogram_bins` to set number of bins (0 to choose a reasonable default)
        - (thanks @paulklemm for the issue #244)
    - stdout pipe/redirect
        - `ls|vd|lpr` to interactively select a list of filenames to send to the printer
        - `q`/`gq` to output nothing
        - `Ctrl+Q` to output current sheet (like at end of -b)
        - `vd -o-` to send directly to the terminal (not necessary if already redirected)
    - plugin framework
        - plugins are optional Python modules that extend or modify base VisiData's functionality
        - this release establishes a structure for creating plugins, and provides an interface within VisiData for installing them
            - `open-plugins` opens the **PluginsSheet**
            - to download and install a plugin, move the cursor to its row and press `a` (add)
            - to turn off a plugin, move the cursor to its row and press `d` (delete).
        - for more information see (https://visidata.org/docs/plugins)
    - deferred changes
        - modifications are now highlighted with yellow, until committed to with `^S` (`save-sheet`)

## interface changes

- `setcol-*`, `dive-selected`, `dup-selected-*`, `columns-selected`, `describe-selected` use only selectedRows (do not use all rows if none selected) #265 (thanks @cwarden)
- `edit-cells` renamed to `setcol-input`
- `fill-nulls` renamed to `setcol-fill`
- `paste-cells` renamed to `setcol-clipboard`
- `dup-cell`/`dup-row` on SheetFreqTable and DescribeSheet renamed to `dive-cell`/`dive-row`
- `next-page`/`prev-page` renamed to `go-pagedown`/`go-pageup`
- `save-col` always saves all rows in current column (instead of selectedRows or rows)
- `copy-*` use only selectedRows, warning if none selected (cmdlog safe)
- `syscopy-*` use only selectedRows, fail if none selected (not cmdlog safe)
- all `plot-selected` are now `plot-numerics`; `plot-numerics` uses all rows
- Shift+S pushes `sheets-stack`; gS pushes `sheets-all`. removed graveyard sheet.
- `random-rows` is no longer bound to any key by default (was Shift+R).
- `freq-summary` was `freq-rows`; adds summary for selected rows
- cmdlog is now based on longname instead of keystrokes
- cmdlog does not log resize commands
- exit with error code on error during replay (suggested by @cwarden #240)
- split `Ctrl+V` (check-version) into `Ctrl+V` (show-version) and `z Ctrl+V` (require-version)
- `show-expr` now unbound from `z=`
- add `options.row_delimiter` (default to `\n`)


## plugins

- vfake: anonymizes columns
- livesearch: filter rows as you search
- rownum: add column of original row ordering
- sparkline: add a sparkline column to visualise trends of numeric cells in a row (thanks @layertwo #297)

## Bugfixes

- [addcol-new] addcol-new now works in batch mode (thanks @cwarden for the bug report #251)
- [canvas] clipstr xname to prevent overlap with 1st element in xaxis
- [color] column separator color applies to regular rows (thanks @mightymiff for bug report #321)
- [DirSheet] delete-selected now deletes all of the selected files upon save-sheet (thanks @cwarden for the bug report #261)
- [display] fix resizing issue with wide chars (thanks @polm for the bug report #279 and for the fix #284 )
- [freqtbl] unselect-rows now updates source rows (thanks @cwarden for bug report #318)
- [go-col-regex] nextColRegex sheet is implicit parameter
- [help] use tab as sep for system sheets (thanks @frosencrantz for bug report #323)
- [plot] graphing currency values now works
- [pyobj] SheetDict nested editing (thanks @egwynn for the bug report #245)
- [txt] TextSheets now save as .txt
- [yaml] handle sources that do not load as lists (thanks @frosencrantz for bug report #327)
- [vdtui] make Sheet sortable (related to an issue found by @jsvine #241)


## Additions and improvements

- [addcol-new] does not ask for column name
- [aggr] add `list` aggregator (thanks @chocolateboy #263)
- [canvas] add legend width to fit max key (thanks @nicwaller for request)
- [chooseMany] error() on invalid choice #169
- [command join] add join-sheets-top2 (`&`) / join-sheets-all (`g&`) to Sheet to join top 2/all sheets in sheets-stack
- [command sort] `sort-*-add` bound to z[] and gz[] to add additional sort columns
- [command syspaste-cells] add `syspaste-cells` to paste into vd from system clipboard (thanks kovasap for PR #258)
- [describe] add `sum` (thanks @pigmonkey for suggestion #315)
- [DirSheet] include folders and hidden files
- [exec-longname] enable history
- [freeze-sheet] only freeze visibleCols
- [html] add links column where hrefs available (suggested by @am-zed #278)
- [license] remove MIT license from vdtui; all code now licensed under GPL3
- [loader fixed] provide a way to limit the max number of columns created (thanks @frosencrantz for suggestion #313)
    - added `options.fixed_maxcols` (default: no limit)
- [loader fixed] loaders override putValue, not setValue (thanks @aborruso for bug report #298)
- [loader jira] add support for jira filetype, a markdown derivative compatible with Atlassian JIRA (thanks @layertwo #301)
- [loader Pyobj] `py` filetype to import and explore a python module: `vd -f py curses`
- [loader pyxlsb] add .xlsb loader (suggested by @woutervdijke #246)
- [loader ndjson ldjson] add as aliases for jsonl
- [loader npy] add .npy loader, including type detection
- [loader npz] add support for .npz index
- [loader usv] add .usv loader
- [macros] is now deprecated
- [motd] domotd is asyncsingle and thus not sync-able
- [mouse] bind Ctrl+scrollwheel to scroll-left/right; change to move cursor by `options.scroll_incr` (like scroll-up/down)
- [mouse] slide columns/rows with left-click and drag
- [openSource] create new blank sheet if file does not exist
- [option json] add `options.json_sort_keys` (default True) to sort keys when saving to JSON (thanks @chocolateboy for PR #262)
- [option regex+] `options.default_sample_size` (default 100) to set number of example rows for regex split/capture (now async).  use None for all rows. (thanks @aborruso #219)
- [option vd] `--config` option to specify visidatarc file (suggested by @jsvine #236)
- [option vdtui] remove `curses_timeout` option (fix to 100ms)
- [pandas] support multi-line column names (suggested by @jtrakk #223)
- [pandas] implement sort() for pandas DataFrame (suggested by @migueldvb #257)
- [pandas] use value_counts() for PandasSheetFreqTable (thanks @azjps for PR #267)
- [pandas] selection support for PandasSheet (thanks @azjps for PR #267)
- [pandas] reset index (thanks @danlat #277)
- [pandas] if the df contains an index column, hide it
- [pcap] adds saver for .pcap to json (thanks @layertwo for PR #272)
- [perf] expr columns are now set to cache automatically
- [perf] drawing performance improvements
- [perf] minor improvements to cliptext
- [perf] several minor optimisations to color
- [precious] describe-sheet is now precious; error-sheet and threads-sheet are not
- [replay] show comments as status (suggested by @cwarden)
- [save] make all `save_` callers async
- [sqlite] add save (CREATE/INSERT only; for wholesale saving, not updates)
- [sqlite] `Ctrl+S` to commit add/edit/deletes
- [sqlite] add support for .sqlite3 extension
- [tar] add support for opening tar files (thanks @layertwo #302)
- [vdmenu] `Shift+V` opens menu of core sheets
    - press `Enter` to open sheet described in current row
- [win] several changes made for increased windows-compatibility (thanks @scholer!)
- [yaml] bump min required version (thanks @frosencrantz for suggestion #326)


## API
- VisiData, BaseSheet, Column inherit from Extensible base class
  - internal modules and plugins can be self-contained
  - `@X.property @X.lazy_property`, `X.init()`, `@X.api`
- remove Sheet.vd; 'vd' attrs now available in execstr
- remove hooks
- add @deprecated(ver) decorator; put deprecations in deprecated.py
- `vd.sync(*threads)` waits on specific threads (returned by calls to `@asyncthread` functions)
- add Sheet.num for left status prompt
- pivot and frequency table have been consolidated for numeric binning
- add Sheet.nFooterRows property
- Sheet.column() takes colname instead of regex; add Sheet.colsByName cached property
- use addRow to rows.append in reload()
- Selection API is overloadable for subclasses of Sheet whose rows don't have a stable id() (like pandas)
- use locale.format_string and .currency
    - uses user default locale settings at startup
    - changes fmtstr back to %fmt (from {:fmt})
- vdtui broken apart into separate modules: editline, textsheet, cliptext, color, column, sheet
    - much code reorganization throughout
- convert all `vd()` to `vd`
- remove ArrayColumns, NamedArrayColumns
- urlcache now takes days=n
- Sheet.rowid
- add windowWidth and windowHeight
    - Sheets use their own .scr, in preparation for split-screen
- add VisiData.finalInit() stage
    - call vd.finalInit() at end of module imports to initialise VisiData.init() members
    - so that e.g. cmdlog is not created until all internal sheet-specific options has been set
- remove replayableOption() (now replay an argument within option())
- CursesAttr is now ColorAttr; ColorAttr now a named tuple
    - variables that contain a ColorAttr have been renamed from attr to cattr for readability
- improvements to scrolling API
- rename most cases of Sheet*/Column* to *Sheet/*Column
- use pathlib.Path in visidata.Path
- remove BaseSheet.loaded; add BaseSheet.rows = UNLOADED
- vd.push no longer returns sheet
- add @asyncsingle for asyncthread singleton

## Deps
- add submodule fork of pyxlsb for VisiData integration
- add amoffat/sh as submodule for vgit and vsh
- [postgres] swap for binary version of dep


# v1.5.2 (2019-01-12)

## Bugfixes
- [regex] fix `g*` #239 (thanks to @jsvine for bug hunting)
- [editline] suspend during editline will resume in editline
- [editline] `Ctrl+W` on an empty value in editline now works

## Docs
- [manpage] update the manpage to be more accurate for boolean command line options

# v1.5.1 (2018-12-17)

## Bugfixes
- [canvas] fix mouse right-click and cursor movement on canvas
- [idle performance] fix regression
- [columns] fix editing of "value" column on ColumnsSheet
- [describe] fix colorizer inheritance
- [csv] always create at least one column
- [pandas] fix pandas eval (`=`, etc) #208 (thanks to @nickray for suggesting)
- [pandas] preserve columns types from DataFrame #208 (thanks to @nickray for suggesting)
- [pandas] remove data autodetect #208 (thanks to @nickray for suggesting)

## Additions and changes
- [selection] `options.bulk_select_clear` per #229 (thanks to @aborruso for suggesting)
- [setcol-subst-all] add `gz*` to substitute over all visible cols (thanks to @aborruso for suggesting)
- [options] Shift+O now global options (was sheet options); `zO` now sheet options; `gO` now opens .visidatarc which can be edited (was global options)
- [sort] orderBy now asynchronous #207 (thanks to @apnewberry for suggesting)
- [fill] fill now async; uses previous non-null regardless of selectedness #217 (thanks to @aborruso for suggesting)
- [pandas] `options.pandas_filetype_*` passed to `pandas.read_<filetype>` (like `csv_*` to Python `csv` module) # 210 (thanks to @pigmonkey for suggesting)
- [rename-col-selected] `z^` now renames the current column to contents of selected rows (previously `gz^`); `gz^` now renames all visible columns to contents of selected rows #220 (thanks to @aborruso for suggesting)
- [vdtui null] show null display note in cells which match `options.null_value` (was only for None) # 213 (thanksto @aborruso for suggesting)
- [vdtui] visidata.loadConfigFile("~/.visidatarc") for use in REPL #211 (thanks to @apnewberry for suggesting)
- [progress] include thread name on right status during async
- [progress] add gerund to display (instead of threadname)
- [http] user specified filetype overrieds mime type
    - e.g. `vd https://url.com/data -f html`
- [clipboard] use `options.save_filetype` for default format



# v1.5 (2018-11-02)

## Bugfixes
- [clipboard] fix broken `gzY` (syscopy-cells)
- [cmdlog] always encode .vd files in utf-8, regardless of options.encoding
- [tsv] major `save_tsv` performance improvement
- [tsv] make short rows missing entries editable
- [shp] reset columns on reload
- [graph] shift rightmost x-axis label to be visible
- [http] allow CLI urls to have `=` in them
- [fixed width] truncate cell edits on fixed width sheets
- [aggregators] ignore unknown aggregators
- `visidata.view(obj)`: obj no longer required to have a `__name__`

## Additions and changes
- [save tsv json] errors are saved as `options.safe_error` (default `#ERR`)
   - if empty, error message is saved instead
- [plugins] `~/.visidata` added to sys.path on startup
   - put plugin in `~/.visidata/vdfoo.py`
   - put `import vdfoo` in `.visidatarc` to activate
- [aggregators] show-aggregate (`z+`) now aggregates selectedRows
- [tsv] add unnamed columns if extra cells in rows
- [diff] now based on display value (more intuitive)
- [mouse] move to column also
- [mouse] right-click to rename-col, rename-sheet, or edit-cell
- [cosmetic] addcol-new (`za`) input new column name on top of new column
- [cosmetic] include file iteration in progress meter
- [xls xlsx] use options.header to determine column names

# v1.4 (2018-09-23)

## Bugfixes

- batch mode with no script should use implicit stdin only if no other files given (Closed #182)
- [pivot] pivot keycolumn copy was yielding strange nulls
- [join] fix extend join
- [csv] include first row in file even if `options.header` == 0
- [sysclip] fix bug where `gzY` did not copy selected rows (Closed #186)
- [motd] fix bug with disabling `options.motd_url` (Closed #190)

## Additions and changes

- various improvements in performance and in CPU usage (Closed #184, #153)
- [pyobj] `visidata.view(obj)` and `visidata.view_pandas(df)`
- [pandas] `-f pandas` loads file with `pandas.read_<ext>`
- [TextSheet] wrap made consistent with new options
- [date] date minus date now gives float number of days instead of seconds
- [pcap] add support for reading pcapng (thanks @layertwo!)
- [setcol] limit `gz=` range parameters to the number of rows selected to be filled (thanks @ssiegel!)
- [anytype] format anytype with simple str()

# v1.3.1 (2018-08-19)

- [http] add `tab-seperated-values` to content_filetypes mapping
- [join] add `extend` join type to use keep all rows and retain **SheetType** from first selected sheet
- `rename-sheet` renames current sheet to input
- [json] add options.json_indent for pretty-printing
- [tsv json txt] add options.save_errors (default True) to include errors when saving
- remove all options.foo_is_null and fix according to 178-nulls.md
- add `z^C` and `gz^C` to cancel threads in current/selected row(s)
- [bugfix] `^R` (reload) on a filtered sheet (`"`) now reloads only the filtered rows
- [aggregators] fix summation with exceptions
- [DirSheet] add `gz^R` (reload-rows) to undo modifications on selected rows

# v1.3 (2018-08-11)

- commands overhaul; see `commands.tsv` (command longnames should now be largely stable)
- add quantile aggregators (q3/q4/q5/q10)
- add `z;` to add new column from bash *expr*, with `$`columnNames as variables
- keyboard layout (thanks to @deinspanjer for the inspiration)
- `O` launches sheet-specific options (see design/169.md); `gO` launches global OptionsSheet
- options.wrap now defaults to False
- options.quitguard enables confirmation before quitting last sheet
- options.safety_first makes loading/saving more robust, at the cost of performance
   - currently only removing NULs from csv input
- dedup, sort, color status messages by "priority" (thanks to @jsvine for suggestion)
- remove menu system
- can now edit source values from FreqSheet

- Command changes
    - `^H` is now main command to open the manpage!  `z^H` opens a list of all commands for this sheet.
    - `R` (`random-sheet`) pushes sheet instead of selecting (reverting to former behavior)
    - `za` (`addcol-empty`) asks for column name
    - `zd` (`delete-cell`) moves value to clipboard ("cut", like other delete commands)
    - add `gI` (`describe-all`) like `gC` (`columns-all`)
    - add `gS` (`sheets-graveyard`)
    - add `g(`, `z(`, `gz(` variants of `(` 'expand-column'
    - add `z|` and `z\` to un/select by python expr (thanks to @jsvine for suggestion)
    - add `z#` to set type of current column to `len`
    - add `z;` to get the stdout/stderr from running a cmdline program with $colname args
    - `Space` is now bound to exec-longname (was `menu`; `^A` was exec-longname previously)

- Loaders:
    - add pandas adapter
    - add xml loader
    - add pcap loader (thanks to @vbrown608 and @TemperedNetworks)
    - add yaml loader (thanks to @robcarrington, @JKiely, @anjakefala at PyCon Sprints for making this happen)
    - add jsonl saver
    - remove `tsv_safe_char` and split into `tsv_safe_newline` and `tsv_safe_tab`

- initial commit of a task warrior app (vtask)

## minor changes
- more portable system clipboard handling (thanks @chocolateboy for PR)
- [json] no more incremental display during loading (need better json parser than stdlib)
- `date` supports adding a number of days (or `6*hours`, `9*months`, etc)
- hidden columns are darkened on columns sheet
- exception rollup
- dev/commands.tsv table of commands
- motd default url uses https
- improve ProfileSheet
- [DirSheet] editable `mode` (set to octal like `0o0644`)

# v1.2.1 (2018-07-05)

- python 3.7
    - Change `async` decorator to `asyncthread` and rename `async.py` to avoid using Python 3.7 keyword

# v1.2 (2018-04-30)

- macro system
   - `gD` goes to directory browser of `options.visidata_dir` (default to `~/.visidata/`) which contains saved commandlogs and macros
   - `z^S` on CommandLog saves selected rows to macro for given keystroke, saving to `.visidata/macro/command-longname.vd`
   - macro list saved at `.visidata/macros.vd` (keystroke, filename)
- `options.cmdlog_histfile` for auto-appended (default: empty means disabled)
- [DirSheet] edits make deferred changes to any field
   - add `directory` and `filetype` columns
   - note: only 256 changes maintained per column (same as column cache size)
   - `^S` saves all deferred changes
   - `z^S` saves changes for the current file only
   - `^R` clears all changes (reload)
   - `z^R` clears changes on the current file only
   - `d`/`gd` marks the current/selected file for deletion
   - if `directory` is edited, on `^S` (save) file is moved (if directory not existing, a new directory is created)
- [New conda package](https://github.com/conda-forge/visidata-feedstock)
- add .visidatarc [snippets](https://github.com/saulpw/visidata/tree/stable/snippets) with examples of extra functionality
- add replayable options [#97](https://github.com/saulpw/visidata/issues/97)
- `g^S` for multisave to single file (`.html`, `.md` and `.txt` are currently supported) or directory
- `z^S` to save selected rows of current column only (along with key columns)
- `T` to transpose rows and columns [#129](https://github.com/saulpw/visidata/issues/129)
- `^A` to specify a command longname to execute
- `^O`/`g^O` to open current/selected files in external editor
- `g^R` on SheetsSheet to reload all [selected] sheets
- `options.error_is_null` to treat errors as nulls when applicable
- `g,` fixed to compare by visible column values, not by row objects
- `gv` to unhide all columns
- `gM` open melted sheet (unpivot) with key columns retained and *regex* capture groups determining how the non-key columns will be reduced to Variable-Value rows
- `g*` replace selected row cells in current column with regex transform
- Shift-Up/Down aliases for mac [#135](https://github.com/saulpw/visidata/issues/135)
- options.wrap now true by default on TextSheet (`v` to toggle)
- `save_txt` with single column concatenates all values to single file
- `+` can add multiple aggregators
- ^X bugfix: use evalexpr over cursorRow
- `z`/`gz` `s`/`t`/`u` to select to/from cursorRow
- `z<` and `z>` to move up/down to next null cell
- `"` no longer reselects all rows
- `sheet-set-diff` command to act like `--diff`
- math functions (like sin/cos) now at toplevel
- bugfix: freeze
- all `csv_` options sent to csv.reader/writer
- `options.tsv_safe_char` to replace \t and \n in tsv files; set to empty for speedup during tsv save
- loaders and savers
    - support bz2 and xz (LZMA) compression (thanks @jpgrayson)
    - add loaders for:
        - sas7bda (SAS; requires `sas7bdat`)
        - xpt (SAS; requires `xport`)
        - sav (SPSS; requires `savReaderWriter`)
        - dta (Stata; requires `pandas`)
    - .shp can save as .geojson
    - add htm as alias for html filetype
    - json bugfix: fix [#133](https://github.com/saulpw/visidata/issues/133) json loader column deduction
- [experimental] bin/vsh initial commit

# v1.1 (2018-03-05)

- VisiData will be included in the [next debian repository release](https://tracker.debian.org/pkg/visidata)!
- remove all install dependencies
  - additional libraries must be installed manually for certain loaders; see requirements.txt
- experimental hierarchical menu system with SPACE to explore commands
    - use standard movement keys (`hjkl`/`arrows`) to navigate within a command level
    - Use `Enter`/`q` to navigate down/up a command tree
    - abort with `gq` or `^C`
    - existing chooseOne selections (aggregators/joins) still use simple input() for now
    - most longnames changed
        - let me know if anyone is using any longnames at all, and we will stabilize the names
    - if you do end up playing with it, please let me know what did and didn't work for you
- randomized message/announcement/tip on startup; disable with `options.motd_url = None`
   - cache messages in `$HOME/.visidata/`

Command additions/changes:

- add `za` and `gza` to add 1/N new blanks column
- add `(` and `)` commands to expand/collapse list/dict columns (e.g. in nested json)
- add `Backspace` command to drop sheet like `q` and also scrub its history from the cmdlog
- [canvas] add `d` and `gd` to delete points from source sheet
- remove `!@#$%-_` special actions on columns sheet
- alias Shift+Arrows to `HJKL` (may not work in all environments)
- alias `ENTER` to modify-edit-cell by default
- add `Y`/`gY`/`zY` to copy row/selected/cell to system clipboard (with options.clipboard_copy_cmd)

- filename `-` works to specify stdin/stdout (`-b -o -` will dump final sheet to stdout)
- search/select uses most recent when not given any (as in vim/etc)
- annotate None with disp_note_none ('∅'); previously was not visually distinguishable from empty string

- save to .md org-mode compatible table format
- load/view/edit/save png, edit pixels in data form
- load/view ttf/otf font files
- [canvas] draw quadratic curves with qcurve([(x,y)...])
- improvements/bugfixes: pivot, describe, melt, sqlite, shp, html

# v1.0 (2018-01-24)

- date.__sub__ returns timedelta object (was int days)
- pivot table bugfixes
- many cosmetic fixes
- disable default profiling for perf improvements
- remove .visidatarc support in PWD or XDG; only $HOME/.visidatarc supported now
- website and docs complete overhaul
- do not execute .py files
- apt/brew packages submitted

# v0.99 (2017-12-22)

- tab completion for filename and python expr
- `v` now 'visibility toggle' (moved from `w`)
- `^W` to erase a word in the line editor
- `gC`
- `--version` (thanks to @jsvine)
- `options.use_default_colors` (thanks to @wavexx)
- `median` aggregator
- .html loads tables (requires lxml)
  - simple http works (requires requests)
- json save
- json incremental load
- [cmdlog] use rowkey if available instead of row number; options.rowkey_prefix
- [cmdlog] only set row/col when relevant
- [vdtui] task renamed to thread
- /howto/dev/loader
- /design/graphics

# v0.98.1 (2017-12-04)

- [packaging]
    - make non Python standard library loader dependencies optional
    - provide method for full installation via `pip3 install "visidata[full]"`
- [visidata.org](http://visidata.org)  change copyright in footer
- [docs] add csv dialects to manpage (closes issue #88)
- [bugfix]
    - fix for `^Z` in builtin line editor
    - fixed-width loader needs source kwarg

# v0.98 (2017-11-23)

- [visidata.org](http://visidata.org) revamp

- [canvas] graphs and maps!
    - `.` or `g.` to push a graph or a map from the current sheet (dot=plot)
    - supports .shp and vector .mbtiles
    - mouse left-click/drag to set cursor
    - mouse right-click/drag to scroll canvas
    - scrollwheel to zoom in/out on a canvas
    - `s`/`u` to select/unselect rows at canvas cursor
    - `ENTER` to push source sheet with only rows at canvas cursor
    - 1-9 to toggle display of 'layers' (colors)
    - `_` to zoom out to full width
    - `disp_pixel_random` option chooses pixel attrs at random (weighted), instead of most common
    - `+`/`-` to zoom in/out via keyboard

- Updates to commands
    - Remove ` (backtick) command
    - Remove most zscroll commands (`zs`/`ze`)
        - `zz` moves cursor to center, uncertain about the future of `zt` due to conflict with `t` for toggle
    - `ga` adds N new rows
    - `gz=` sets value for selected/all rows to a Python sequence in this column
    - `z_` sets column width to given value
    - `z-` cuts column width in half
    - `P` is now "paste before" (like vim); `R` now pushes a random sample
    - `^Z` now sends SIGSTOP; `^O` "opens" the external $EDITOR (from builtin line editor)
    - [ColumnsSheet] Added `~!@#$` commands back, to set type of source columns
    - `w` is becoming a more universal "visibility toggle"
        - [TextSheet] `w` toggles wordwrap
        - [canvas] `w` toggles display of the labels
        - [pyobj] `w` toggles hidden properties and methods

- Updates to command line args and options
    - set initial row/col with `+<row#>:<col#>` (numeric only)
    - `--delimiter`/`-d ` option (separate from `--csv-delimiter`) sets delimiter for tsv filetype
    - `--replay-wait`/`-w` renamed from `--delay`/`-d`
    - `disp_date_fmt` option for date display format string (default is date-only)
    - `zero_is_null`/`empty_is_null`/`none_is_null`/`false_is_null` set which values are considered null (previously was `aggr_null_filter`)
    - `--skiplines` option renamed to `--skip`, and `--headerlines` to `--header`

- Design improvements
    - Add specific rowtype for each sheet (see right status)
    - dates are a kind of numeric type (useful for graphing as the x-axis)
    - `use_default_colors` (at behest of @wavexx)
    - more robust Progress indicator
    - populate DescribeSheet in async thread
    - remove default names for unnamed columns
    - history up/down in edit widget now feels right

- API changes
    - change main Column API to getter(col, row) and setter(col, row, val)
    - move Path and subclasses out of vdtui
    - TextSheet source is any iterable of strings
    - Sheet.filetype provides default save filename extension


## 0.97.1 (2017-10-29)
- Fix postgres lazy import
- BugFix: issue #83 - `z?` works on OSX
- BugFix: <Enter> on SheetsSheets itself now does nothing
- Move from readthedocs to visidata.org


## 0.97 (2017-10-05)
- Features
    - [replay]
        - move vdplay into vd --play
        - -p --play now replays scripts live
        - --delay interspaces replay by delay seconds
        - --batch to replay without interface
        - --output to save at end of replay
        - --replay-movement=True has --play move the cursor cell-by-cell
        - -y --confirm-overwrite=False
        - replay scripts can be strformatted with field=value
        - add ^U command to pause/resume playback
        - add ^K to cancel replay
        - add Space command to go to next step of replay while paused
    - [global]
        - remap toggle to 't' (was Space)
        - remap ^Y to push sheet of cursorRow
        - 'A' creates new sheet with N empty columns
        - remap 'r' to regex search of row key
        - add zr/zc to go to row/col number
        - F1/z? now launches man page
        - gF1/gz? now launches commands sheet
        - add `f` command to fill empty cells with the content of a non-empty cell up the current column
        - add Del/gDel to set value(s) to None
        - remove TAB/Shift-TAB sheet cycling
        - add z^ command to set current column name to current cell value
        - add gz^ to set current column name to cell values in selected rows
        - add 'z=' to show computed expression over current row
        - z' adds cache to current column (gz' for all columns)
        - `gh` moves cursor to leftmost column (instead of leftmost non-key column)
    - [aggregators]
        - allow multiple aggregators
        - 'g+ adds an aggregator to selected columns on columns sheets
        - sets the exact set of aggregators on the column sheet with 'e'/'ge'
        - 'z+' displays result of aggregation over selected rows for current column on status
        - rework aggregators so multiple aggregators can be set
    - [sheets sheet]
        - '&' on Sheets sheet is now sole join sheet command; jointype is input directly
        - add sheet concat
    - [columns sheet]
        - remap ~!@#$%^ on Columns sheet to behave like they do on other sheets
        - add g prefix to ~!@#$%^ to operate on all 'selected rows' on Columns sheet (thus modifying column parameters on source sheet)
    - [textsheet]
        - add 'w' command on TextSheets to toggle wrap
    - [cmdlog]
        - editlog renamed to cmdlog
        - cmdlog has a new format which minimises recordings of movement commands
        - '^D' now saves cmdlog sheet
    - [pivot]
        - zEnter pushes this cell; Enter pushes whole row
    - [describe]
        - add DescribeSheet with 'I' command for viewing descriptive statistics
        - add zs/zu/zt/zEnter commands to engage with rows on source sheet which are being described in current cell of describe sheet
    - [frequency]
        - 'zF' provides summary aggregation
    - [metasheets]
        - add hidden source column to metasheets
        - ^P view status history
    - [loaders]
        - add 'postgres' schema for simple loader from postgres:// url
        - add gEnter for .zip file mass open
        - add 'fixed' filetype to use fixed column detector
    - [clipboard]
        - remove `B` clipboard sheet
        - rework all d/y/p commands for only one buffer
        - remove g^Z and gp
    - [options]
        - remove -d debug option
        - add --diff to add colorizer against base sheet
            - diffs a pair of tsvs cell-by-cell
        - theme options removed as CLI arguments (still available for .visidatarc or apps)
        - `'` appends frozen column
        - rename and reorder options
- Community
    - [docs]
        - replace .rst userguide with VisiData [man page](http://visidata.org/man)
    - [visidata.org]
        - update index.html
        - automate creation of tour pages from tours.vd
            - tours will be played and recorded using asciinema
            - then compiled into a .html with mkdemo.py for http://visidata.org/tour
        - upload html version of manpage
- Internals
    - renamed toplevel command() to globalCommand(); removed Sheet.command(); sheet commands now specified in Sheet.commands list of Command() objects at class level
    - setter API now (sheet,col,row,value)
    - move `visidata/addons/*.py` into toplevel package

## 0.96 (2017-08-21)
- data can be piped through stdin
- remap: `N` is now previous match (instead of `p`)
- `:` now regex split
- add `bin/viewtsv` example tsv viewer as an example of a small vdtui application
- add `options.cmd_after_edit` for automove after edit
- add clipboard functionality
    - `y` yanks row at cursor to clipboard; `gy` copies all selected rows
    - `d` deletes row and move to clipboard; `gd` moves all selected rows
    - `p` now pastes the row most recently added to the clipboard after current row; `gp` pastes all rows from clipboard after current row
    - `Shift-B` opens clipboard sheet
    - `Ctrl+z` now undoes the most recent delete; `gCtrl+z` undoes all deletes
- Fix cursor row highlighting of identical rows

## v0.95.2
- move some functionality out of vdtui into separate python files
- add Ctrl+z command to launch external $EDITOR
- add ``options.force_valid_names``

## v0.94 (2017-08-02)
- add options.textwrap for TextSheet
- add vd.remove(sheet)
- Sheet.sources now  modifiable

## v0.93 (2017-07-30)
- fix display/feel bugs in editText
- remove BACKSPACE for editlog undo
- fix colorizer API
- add `ctrl-u` command to toggle profiling of main thread
- fix `C`olumn statistics (`options.col_stats` still disabled by default)

## v0.92 (2017-07-11)
- `F`requency sheet groups numeric data into intervals
   - added `histogram_bins` and `histogram_even_interval` options
   - added `w` command on the sheet that toggles `histogram_even_interval`
- change key for 'eval Python expression as new pyobj sheet' from Ctrl+O to Ctrl+X

## v0.91 (2017-06-28)
- make options automatically typed based on default
- documentation cleanups
- remove R command (set filetype on CLI)

## v0.80
- tour of screenshot.gif
- regex transform now `*` (';' is still regex split)
- Make regex search/select to work more like vim
- Move several non-essential commands out of vd.py
- change license of vd.py to MIT
- vdtutor start
- currency type with `$`; str type moved to `~`; remove type autodetect
- www/ for landing page
- move from .md to .rst for documentation

## v0.61 (2017-06-12)
- colorizers
- `g[` and `g]` to sort by all key columns
- `;` and `.` experimental regex commands

## v0.59 (2017-05-31)
- pivot sheets with `W`
- undo with `BACKSPACE` and replay with `ga`
- dev guide and user guide
- `ge` mass edit
- freeze with `g'`

## v0.44
- creating sustainable dev process at RC
- `z` scrolling prefix

## v0.42
- async select/unselect
- aggregator functions on columns
- .xls

## v0.41 (2017-01-18)
- asynchronous commands (each in its own thread) with
   - `^T` sheet of long-running commands
   - `^C` cancel
   - `ENTER` to see the final performance profile
- `P` random population of current sheet
- headerlines default now 1

## v0.40
- options settable with command-line arguments (`--encoding=cp437`)
- input() histories with UP/DOWN (and viewable with `I`)
- unicode input now works
- editText clears value on first typing
- `"` duplicates sheet with only selected rows; `g"` duplicates entire sheet verbatim

## v0.38
- sortable date
- open_zip comes back

## v0.37
- `g~` (autodetect all columns)
- `"` copies row to immediately following
- nulls, uniques on columns sheet

## v0.36
- right column
- regex subst
- unreverse [/] sort keys ([ = ascending)

## v0.35 (2016-12-04)
- reverse [/] sort keys
- goto `r`ow by number or `c`olumn by name

## v0.33
- type detection with `~`
- date type
- fix outer join

## v0.32
- expose col.type in column header
- push value conversion to time of usage/display

## v0.31
- F1 help sheet
- ^O directly exposes eval result as sheet
- custom editText with initial value, ESC that raises VEscape, and readline edit keys

## v0.30 (2016-11-27)
- make all sheets subclasses of VSheet
- remove .zip opening and url fetching
- added options ColumnStats and csv_header

## v0.29
- pin key columns to left
- join sheets on exact key match
- -r/--readonly mode

## v0.28 (2016-11-22)
- inputs: .csv, .tsv, .json, .hdf5, .xlsx, .zip
- outputs: .csv, .tsv
- hjkl cursor movement, t/m/b scroll to position screen cursor
- skip up/down columns by value
- row/column reordering and deleting
- resize column to fix max width of onscreen row
- filter/search by regex in column
- sort asc/desc by one column
- `g`lobal prefix supersizes many commands
- `e`dit cell contents
- convert column to int/str/float
- reload sheet with different format options
- add new column by Python expression
- `s`elect/`u`nselect rows, bulk delete with `gd`
- `F`requency table for current column with histogram
- `S`heets metasheet to manage/navigate multiple sheets,
- `C`olumns metasheet
- `O`ptions sheet to change the style or behavior
- `E`rror metasheet
- `g^P` status history sheet

## v0.14 (2016-11-13)


================================================
FILE: CLAUDE.md
================================================
# VisiData Development Guide

Quick reference for VisiData development. For detailed coding patterns, conventions, and best practices, see **[dev/STYLE.md](dev/STYLE.md)**.

`CLAUDE.md` and `AGENTS.md` are complementary: use this file for primary contributor workflow and architecture context, and use `AGENTS.md` for concise agent-oriented repository guidance.

## Important Note About AI Usage

VisiData (created in 2016) is 99% written by humans and is NOT a vibe-coded AI project.  This file is meant to allow AI-assisted development of features and bugfixes.  **All code must be reviewed and approved and tested by a human before being merged into the codebase or submitted as a PR.**

## Repository Structure

```
visidata/
├── visidata/              # Main package
│   ├── *.py              # Core modules (sheet.py, column.py, etc.)
│   ├── features/         # Auto-loaded feature plugins
│   ├── loaders/          # File format loaders
│   ├── apps/             # Standalone applications
│   └── experimental/     # Experimental features (load/install with 'import visidata.experimental.foo')
├── tests/                # Test files
├── docs/                 # Documentation
└── dev/                  # Development utilities and docs
```

## Features Directory (`visidata/features/`)

- All `.py` files in this directory are **automatically imported** when VisiData starts
- Each feature file should be self-contained
- Features extend VisiData functionality without modifying core files

## Quick Reference

### Core Classes
- `BaseSheet` - Minimal sheet functionality
- `Sheet` / `TableSheet` - Sheet with columns and rows (most common)
- `Column` - Column definition with getter/setter

### Adding Commands
```python
BaseSheet.addCommand('', 'command-name', 'code', 'help text')
```

### Adding to Global Namespace
```python
vd.addGlobals(MyClass=MyClass)  # Use keyword args, not dict
```

### Adding Menu Items
```python
vd.addMenuItems('''
    Menu > Submenu > Item Name > command-name
''')
```

### Example Feature Structure
```python
from visidata import vd, Sheet, Column

# rowdef: description of what a row represents
class MySheet(Sheet):
    rowtype = 'items'
    columns = [
        Column('name', getter=lambda c,r: r.attribute),
    ]

    def reload(self):
        self.rows = [...]

BaseSheet.addCommand('', 'my-command', 'code', 'help')
vd.addGlobals(MySheet=MySheet)
```

## Development Workflow

1. Add `.py` file to `visidata/features/`
2. Run `vd` and test interactively
3. Iterate and refine
4. Document with docstrings and comments

## Make Targets

- `make test` — run all tests
- `make help` — list all targets

## Documentation

For comprehensive development documentation, see the `dev/` directory:

### [dev/STYLE.md](dev/STYLE.md) - Coding Style and Patterns
Use this when writing code, creating features, or defining sheets and columns.
- Naming conventions (camelCaps, under_score, etc.)
- Feature file structure and patterns
- Sheet and Column class patterns
- Command and menu integration
- API decorators
- Best practices and examples

### [dev/GIT.md](dev/GIT.md) - Version Control Practices
Use this when making commits or preparing pull requests.
- Commit message format and conventions
- Issue tracking in code
- Branch and merge workflow
- Patch-safe commit marking

### [dev/DOCS.md](dev/DOCS.md) - Documentation Writing
Use this when writing user-facing documentation, help text, or in-app guides.
- VisiData's markdown syntax
- Display attribute syntax (colors, clickable links)
- Option and command reference format
- Technical writing guidelines

### [dev/PERFORMANCE.md](dev/PERFORMANCE.md) - Performance Analysis
Use this when investigating or optimizing performance issues.
- Finding reproducible performance issues
- Profiling techniques and tools
- Analyzing profiling results
- Optimization workflow

### [dev/OPTIONS.md](dev/OPTIONS.md) - Options System
Use this when working with options, adding new options, or understanding how configuration resolves.
- Resolution chain (instance → class → global → default)
- How sheets and paths participate in options
- Setting and reading options at different levels

## Updating Documentation

When making **user-facing changes** (new commands, changed behavior, new options, new/changed loaders, UI changes), check [docs/README.md](docs/README.md) to identify which documentation files need to be updated.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
Please don't be a dick ♥


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

## Spread the Word

The single best way you can contribute, is to share your enthusiasm about VisiData with other people.
A vibrant community is essential to its sustainable development.

However, direct and forceful promotion is probably not the most effective approach for a tool like VisiData.
People generally need to be exposed several times and from several sources before they will try some terminal utility they've never heard of before.

Some people are interested, but are daunted by the installation process or the interface; you can [help them get it installed](/install), and provide a few pointers to get started.
Don't make it too complicated or overload them with too many features.
Stick to the basics: arrow keys, quit, help, search, sort, freq table.

We also need people to mention VisiData in their forums and communities that relate to data and terminal programs.
Don't spam or do a drive-by promotion; these are largely ineffective and will often be received negatively.
Endorsements have more weight from people who actively post about other relevant topics; we don't want to become the "VisiData Brigade".

Finally, if you are on "Web 2.social", you can post a [tweet](https://twitter.com/visidata) or a [tutorial]() or a [demo](https://www.youtube.com/watch?v=N1CBDTgGtOU) or [host a workshop](https://www.meetup.com/pt-BR/Journocoders/events/258035880/), or anything else you think might make people interested in exploring the wonderful world of VisiData.

## Support on Patreon

If VisiData saves you time on a regular basis, and especially if VisiData makes your paid work easier, please contribute to [my Patreon](https://www.patreon.com/saulpw).

## Start a Project Using VisiData

If you know Python and want to augment it to suit your own workflow, you can create a loader or a plugin.  In support of this, I have written [a detailed api guide for VisiData](https://www.visidata.org/docs/api/).

Here are some great examples:

  - [jsvine's custom visidata plugins](https://github.com/jsvine/visidata-plugins)
  - [layertwo's pcap loader](https://github.com/saulpw/visidata/blob/develop/visidata/loaders/pcap.py)

Without fail, these projects lead to discovering bugs and help flesh out the API, which result in design improvements in VisiData.
Importantly, each issue found this way comes with real world motivations, so it is easy to explain your reasoning behind proposals and core feature requests.

## Feature Requests

VisiData is designed to be extensible, and most feature requests can be implemented as a one line command, or a tiny snippet of code to include in a `.visidatarc`.

If this would require changes to the VisiData core, and a reasonable design is approved, then the issue can stay open until the core changes have been made.
Otherwise, in the spirit of Marie Kondo, the issue will be closed without prejudice.

Feature requests with some amount of working Python code are more likely to get attention.
Design proposals with concrete use cases are very welcome.

## Writing a well constructed bug report

If you encounter any bugs or have any problems, please [create an issue on GitHub](https://github.com/saulpw/visidata/issues).

A great bug report will include:

  - a stacktrace, if there is an unexpected error; the most recent full stack traces can be viewed with `Ctrl+E` (then saved with `Ctrl+S`)
  - a [.vd](http://visidata.org/docs/save-restore/) and sample dataset that reproduces the issue
  - a .png/.gif (esp. for user interface changes)

Some examples of great bug reports:

  - [#350 by @chocolateboy](https://github.com/saulpw/visidata/issues/350)
  - [#340 by @Mikee-3000](https://github.com/saulpw/visidata/issues/340)


## Setting Up Git Hooks

VisiData includes git hooks in `dev/hooks/` that run unit tests and cmdlog tests before pushing. To enable them:

```
git config core.hooksPath dev/hooks
```

## Submitting Source Code

Check out the [Plugin Authors Guide](https://visidata.org/docs/api) for an overview of the API.
Code in `visidata/features/` or `visidata/loaders/` is generally welcome, as long as it is useful to someone and safe for everyone.
Updates or additions to the core code should be proposed via an [Github Issue](https://github.com/saulpw/visidata/issues/new/choose) before submitting a PR.

VisiData has two main branches:

  - [stable](https://github.com/saulpw/visidata/tree/stable) has the last known good version of VisiData (what is in pypi/brew/apt).
  - [develop](https://github.com/saulpw/visidata/tree/develop) has the most up-to-date version of VisiData (which will eventually be merged to stable).

All pull requests should be submitted against `develop`. Submitters will need to e-sign a [Copyright Assignment Agreement (CAA)](https://visidata.org/caa) before a pull request will be accepted.

# Open Source License and Copyright

VisiData is an open-source utility that can be installed and used for free (under the terms of the [GPL3](https://www.gnu.org/licenses/gpl-3.0.en.html)).

The core VisiData utility and rendering library will always be both free and libre.

As the copyright holder, Saul Pwanson has the authority to negotiate other license terms.

**By submitting changes to this repository, you acknowledge that you assign copyright to the owner of the repository ([Saul Pwanson <vd@saul.pw>](mailto:vd@saul.pw)).**


================================================
FILE: Dockerfile.alpine
================================================
FROM python:3.8-alpine

RUN pip install requests python-dateutil wcwidth

RUN mkdir -p /opt/visidata
WORKDIR /opt/visidata
COPY . ./
RUN sh -c 'yes | pip install -vvv .'

ENV TERM="xterm-256color"
ENTRYPOINT bin/vd


================================================
FILE: Dockerfile.darkdraw.alpine
================================================
FROM visidata

RUN apk add git
RUN pip install git+https://github.com/devottys/darkdraw.git@master
RUN sh -c "echo >>~/.visidatarc import darkdraw"

ENV TERM="xterm-256color"
ENTRYPOINT ["/opt/visidata/bin/vd", "-f", "ddw"]


================================================
FILE: LICENSE.gpl3
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its user
Download .txt
gitextract_wcvjea1d/

├── .codespellrc
├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   ├── launch.json
│   ├── post-create.sh
│   └── settings.json
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature-request.md
│   │   ├── loader-request.md
│   │   └── question.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── main.yml
│       ├── vdsql.yml
│       └── vgit.yml
├── .gitignore
├── .gitmodules
├── .gitpod.Dockerfile
├── .gitpod.yml
├── .mailmap
├── .theia/
│   └── settings.json
├── AGENTS.md
├── CHANGELOG.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile.alpine
├── Dockerfile.darkdraw.alpine
├── LICENSE.gpl3
├── MANIFEST.in
├── Makefile
├── README.md
├── bin/
│   ├── filter-doc.py
│   ├── vd
│   ├── vd2to3.vdx
│   └── viewtsv.py
├── dev/
│   ├── DOCS.md
│   ├── GIT.md
│   ├── OPTIONS.md
│   ├── PERFORMANCE.md
│   ├── README.md
│   ├── STYLE.md
│   ├── TESTING.md
│   ├── build-container
│   ├── checklists/
│   │   ├── add-aggregator.md
│   │   ├── add-command.md
│   │   ├── feature.md
│   │   ├── manual-tests.md
│   │   └── release.md
│   ├── debian/
│   │   ├── changelog
│   │   ├── control
│   │   ├── copyright
│   │   ├── manpages
│   │   ├── rules
│   │   ├── source/
│   │   │   ├── format
│   │   │   └── local-options
│   │   ├── upstream/
│   │   │   └── metadata
│   │   └── watch
│   ├── design/
│   │   ├── 000-notes.md
│   │   ├── 160-longnames.md
│   │   ├── 169-settings.md
│   │   ├── 173-benchmark.md
│   │   ├── 174-keycols.md
│   │   ├── 175-design-terms.md
│   │   ├── 176-miscrules.md
│   │   ├── 181-benchmark-data.md
│   │   ├── 232-input.md
│   │   ├── 260-push.md
│   │   ├── README.md
│   │   └── path.md
│   ├── diff-test.sh
│   ├── formats.jsonl
│   ├── formats.vd
│   ├── hooks/
│   │   └── pre-push
│   ├── mkman.sh
│   ├── mkpandas-df.py
│   ├── quit.vdx
│   ├── requirements-dev.txt
│   ├── run-tests-individually.sh
│   ├── test-all.sh
│   ├── test-stdin-replay.vdx
│   ├── test.sh
│   ├── types.jsonl
│   ├── vduplot.vdx
│   ├── visidata-brew.rb
│   ├── workshop-outline.md
│   ├── zsh-completion.in
│   └── zsh-completion.py
├── docs/
│   ├── README.md
│   ├── api/
│   │   ├── Makefile
│   │   ├── _static/
│   │   │   ├── .gitkeep
│   │   │   └── css/
│   │   │       └── custom.css
│   │   ├── async.rst
│   │   ├── canvas.rst
│   │   ├── columns.rst
│   │   ├── commands.rst
│   │   ├── conf.py
│   │   ├── data.rst
│   │   ├── extensible.rst
│   │   ├── guides.rst
│   │   ├── index.rst
│   │   ├── interface.rst
│   │   ├── loaders.rst
│   │   ├── make.bat
│   │   ├── modify.rst
│   │   ├── modules.rst
│   │   ├── options.rst
│   │   ├── plugins.rst
│   │   ├── requirements.txt
│   │   ├── runtime.txt
│   │   ├── sheets.rst
│   │   └── style.rst
│   ├── casts/
│   │   ├── expand-cols.cast
│   │   ├── pivot-graphs.cast
│   │   ├── pivot.cast
│   │   ├── save-restore.cast
│   │   ├── split-regex.cast
│   │   └── types.cast
│   ├── colors.md
│   ├── columns.md
│   ├── contributing.md
│   ├── crud.md
│   ├── customize.md
│   ├── dirsheet.md
│   ├── edit.md
│   ├── formats.md
│   ├── freq.md
│   ├── gmail.md
│   ├── graph.md
│   ├── graphics.md
│   ├── group.md
│   ├── index.md
│   ├── internal_formats.md
│   ├── join.md
│   ├── loading.md
│   ├── macros.md
│   ├── menu.md
│   ├── mouse.md
│   ├── move.md
│   ├── navigate.md
│   ├── pipes.md
│   ├── plugins.md
│   ├── rows.md
│   ├── save-restore.md
│   ├── shell.md
│   ├── split.md
│   ├── test.md
│   ├── usage.md
│   └── viewtsv.md
├── platform/
│   ├── windows/
│   │   ├── vdwin-installer.md
│   │   ├── vdwin.py
│   │   └── vdwin.spec
│   └── www/
│       ├── Dockerfile
│       ├── Makefile
│       └── visidatarc
├── plugins/
│   ├── geocoding.py
│   ├── pcap/
│   │   ├── iana-ports.tsv
│   │   └── wireshark-oui.tsv
│   └── vtask.py
├── pyrightconfig.json
├── requirements.scm
├── requirements.txt
├── ruff.toml
├── sample_data/
│   ├── 1794685.fec
│   ├── 2m.tsv
│   ├── StatusPR.csv
│   ├── UTF-8-demo.txt
│   ├── a.tsv
│   ├── accidents.sav
│   ├── arrays.hdf5
│   ├── arrays.npz
│   ├── b.tsv
│   ├── benchmark.arrow
│   ├── benchmark.csv
│   ├── benchmark.fixed
│   ├── benchmark.jsonl
│   ├── benchmark.lsv
│   ├── benchmark.npy
│   ├── benchmark.ods
│   ├── benchmark.psv
│   ├── benchmark.xml
│   ├── benchmark.yml
│   ├── brakes.xpt
│   ├── co3.dta
│   ├── co3.stata
│   ├── color-merged-cells.xlsx
│   ├── countries
│   ├── date-error-test.xlsx
│   ├── empty-cell.xlsx
│   ├── empty-table.html
│   ├── errors.csv
│   ├── freshwater-mammals.toml
│   ├── goog.npy
│   ├── gtm.f5log
│   ├── hello.mnu
│   ├── issue1308.html
│   ├── messenger.pcap
│   ├── numeric-cols.tsv
│   ├── officials.jsonla
│   ├── pr2815.jsonl
│   ├── sample-sales-reps.xlsx
│   ├── sample.arrow
│   ├── sample.arrows
│   ├── sample.conllu
│   ├── sample.geojson
│   ├── sample.parquet
│   ├── sample.tsv
│   ├── sample.vds
│   ├── saulpw-008.xd
│   ├── sensors.h5
│   ├── shapefile/
│   │   ├── WA_State_Boundary.cpg
│   │   ├── WA_State_Boundary.dbf
│   │   ├── WA_State_Boundary.prj
│   │   ├── WA_State_Boundary.shp
│   │   ├── WA_State_Boundary.shx
│   │   └── WA_State_Boundary.xml
│   ├── smiths-json.grep
│   ├── smiths-standard.grep
│   ├── states.yml
│   ├── sunshinelist.html
│   ├── test-fixed-leadingspaces.txt
│   ├── test-unicode-display.tsv
│   ├── test.fixed
│   ├── test.jsonl
│   ├── test.msgpack
│   ├── test.msgpackz
│   └── test.usv
├── setup.py
├── snippets/
│   ├── gender.py
│   ├── geolocate.py
│   └── rank.py
├── tests/
│   ├── .visidata/
│   │   └── .gitignore
│   ├── .visidatarc
│   ├── 1410a.vdj
│   ├── 1410b.vdj
│   ├── 1410c.vdj
│   ├── add-col-dup-attrs.vdj
│   ├── addcol-iter.vdj
│   ├── addcol_window.vd
│   ├── aggregators-cols.vdj
│   ├── aggregators-errors.vd
│   ├── aggregators-set.vd
│   ├── append.vd
│   ├── avg-nulls.vd
│   ├── bulk-rename-cols.vd
│   ├── capture-col-named.vd
│   ├── column-copy.vd
│   ├── column-name-__name__.csv
│   ├── column-name-__name__.vd
│   ├── column-name-_fields.csv
│   ├── column-name-_fields.vd
│   ├── column-name-length.csv
│   ├── column-name-length.vd
│   ├── concat-origin.vdx
│   ├── curcol.vd
│   ├── data1.tsv
│   ├── data2.tsv
│   ├── data3.tsv
│   ├── date_add.vd
│   ├── describe-error.vd
│   ├── describe-through.vd
│   ├── describe.vd
│   ├── diff-join.vdj
│   ├── dup-rows-attrs.vd
│   ├── edit-fixed.vdx
│   ├── edit-joinkey-1.vd
│   ├── edit-joinkey-2.vd
│   ├── edit-joinregular-1.vd
│   ├── edit-joinregular-2.vd
│   ├── edit-type.vd
│   ├── empty-unfurl-2.vd
│   ├── empty-unfurl.vd
│   ├── error-passthru.vd
│   ├── errors-311.vd
│   ├── exp-digits.vdj
│   ├── extend.vd
│   ├── fill-nested.vdj
│   ├── fill-zero.json
│   ├── fill-zero.vdj
│   ├── fill.vdj
│   ├── format-enum-freeze.vd
│   ├── format-enum.vd
│   ├── freeze-json.vd
│   ├── freeze-sheet-attrs.vd
│   ├── freq-dive-except.vdx
│   ├── freq-error.vd
│   ├── freq-fmtstr.vd
│   ├── freq-same-int.vd
│   ├── freq-summary.vd
│   ├── freqtbl-nested-dive-selected.vdx
│   ├── freqtbl-openrow.vd
│   ├── frozen-attrs.vd
│   ├── full-join.vd
│   ├── gMelt.vd
│   ├── getitem.vd
│   ├── golden/
│   │   ├── 1410a.tsv
│   │   ├── 1410b.tsv
│   │   ├── 1410c.tsv
│   │   ├── add-col-dup-attrs.tsv
│   │   ├── addcol-iter.tsv
│   │   ├── addcol_window.tsv
│   │   ├── aggregators-cols.tsv
│   │   ├── aggregators-errors.tsv
│   │   ├── aggregators-set.tsv
│   │   ├── append.tsv
│   │   ├── avg-nulls.tsv
│   │   ├── bulk-rename-cols.tsv
│   │   ├── capture-col-named.tsv
│   │   ├── column-copy.tsv
│   │   ├── column-name-__name__.csv
│   │   ├── column-name-_fields.csv
│   │   ├── column-name-length.csv
│   │   ├── concat-origin.tsv
│   │   ├── curcol.tsv
│   │   ├── date_add.tsv
│   │   ├── describe-error.tsv
│   │   ├── describe-through.tsv
│   │   ├── describe.tsv
│   │   ├── diff-join.tsv
│   │   ├── dup-rows-attrs.tsv
│   │   ├── edit-fixed.fixed
│   │   ├── edit-joinkey-1.tsv
│   │   ├── edit-joinkey-2.tsv
│   │   ├── edit-joinregular-1.tsv
│   │   ├── edit-joinregular-2.tsv
│   │   ├── edit-type.tsv
│   │   ├── empty-unfurl-2.tsv
│   │   ├── empty-unfurl.tsv
│   │   ├── error-passthru.tsv
│   │   ├── errors-311.tsv
│   │   ├── exp-digits.tsv
│   │   ├── extend.tsv
│   │   ├── fill-nested.jsonl
│   │   ├── fill-zero.json
│   │   ├── fill.jsonl
│   │   ├── format-enum-freeze.tsv
│   │   ├── format-enum.json
│   │   ├── format-enum.tsv
│   │   ├── freeze-json.tsv
│   │   ├── freeze-sheet-attrs.tsv
│   │   ├── freq-dive-except.tsv
│   │   ├── freq-error.tsv
│   │   ├── freq-fmtstr.tsv
│   │   ├── freq-same-int.tsv
│   │   ├── freq-summary.tsv
│   │   ├── freqtbl-nested-dive-selected.tsv
│   │   ├── freqtbl-openrow.tsv
│   │   ├── frozen-attrs.tsv
│   │   ├── full-join.tsv
│   │   ├── gMelt.tsv
│   │   ├── getitem.tsv
│   │   ├── hide-uniform.tsv
│   │   ├── histogram.tsv
│   │   ├── import-python.tsv
│   │   ├── inner-join.tsv
│   │   ├── invalid_unicode_sqlite.tsv
│   │   ├── issue1308.tsv
│   │   ├── issue1346.tsv
│   │   ├── issue1377.tsv
│   │   ├── issue1377b.tsv
│   │   ├── issue2015.tsv
│   │   ├── issue2190.tsv
│   │   ├── issue2227.tsv
│   │   ├── issue2316.tsv
│   │   ├── issue2476.csv
│   │   ├── issue2524-curcol.tsv
│   │   ├── issue350.tsv
│   │   ├── issue655.tsv
│   │   ├── issue733.tsv
│   │   ├── issue964-inner-join.tsv
│   │   ├── join-cols-single-sheet.tsv
│   │   ├── join-concat.tsv
│   │   ├── join-different-types.tsv
│   │   ├── join-extend-mult.tsv
│   │   ├── join-merge.tsv
│   │   ├── join-non-unique-cols.tsv
│   │   ├── listofdictobj.tsv
│   │   ├── load-2d-matrix.tsv
│   │   ├── load-conllu.jsonl
│   │   ├── load-csv.tsv
│   │   ├── load-dir.tsv
│   │   ├── load-fec.tsv
│   │   ├── load-fixed-header0.tsv
│   │   ├── load-fixed-leadingspaces.tsv
│   │   ├── load-fixed.tsv
│   │   ├── load-grep-json.grep
│   │   ├── load-grep-standard.grep
│   │   ├── load-h5.tsv
│   │   ├── load-html.tsv
│   │   ├── load-http-flaky.tsv
│   │   ├── load-json.tsv
│   │   ├── load-jsonla.tsv
│   │   ├── load-lsv.lsv
│   │   ├── load-lsv.tsv
│   │   ├── load-msgpack.tsv
│   │   ├── load-msgpackz.tsv
│   │   ├── load-numpy.tsv
│   │   ├── load-ods.tsv
│   │   ├── load-pandas-2.csv
│   │   ├── load-pandas-3.tsv
│   │   ├── load-pandas.tsv
│   │   ├── load-parquet.tsv
│   │   ├── load-png.tsv
│   │   ├── load-sqlite-view.tsv
│   │   ├── load-sqlite.tsv
│   │   ├── load-stata.tsv
│   │   ├── load-toml.tsv
│   │   ├── load-usv.tsv
│   │   ├── load-xlsx.tsv
│   │   ├── load-xml.tsv
│   │   ├── load-xpt-n311.tsv
│   │   ├── load-yaml.tsv
│   │   ├── load-zip.tsv
│   │   ├── load_npy.tsv
│   │   ├── load_xlsx.tsv
│   │   ├── melt-error.tsv
│   │   ├── messenger-nosave.dot
│   │   ├── monthly-revenue.tsv
│   │   ├── numeric-cols.tsv
│   │   ├── numeric-names.tsv
│   │   ├── numeric_binning.tsv
│   │   ├── outer-join-1.tsv
│   │   ├── outer-join-2.tsv
│   │   ├── pandas_loader_dup_selected.tsv
│   │   ├── petsdiet.tsv
│   │   ├── pivot-error.tsv
│   │   ├── pivot-noaggr.tsv
│   │   ├── pivot.tsv
│   │   ├── pr2302.tsv
│   │   ├── pr2308.json
│   │   ├── pr2372.tsv
│   │   ├── pr2400.tsv
│   │   ├── pr2614.tsv
│   │   ├── pr2647.tsv
│   │   ├── pr2688a.tsv
│   │   ├── pr2688b.tsv
│   │   ├── pr2815.json
│   │   ├── pr2855.tsv
│   │   ├── prefer-visible-col.tsv
│   │   ├── pull2140.tsv
│   │   ├── rank-sheetrank-sorted-cols.tsv
│   │   ├── record-aggr.tsv
│   │   ├── remove-errors.tsv
│   │   ├── rows-select-expr.tsv
│   │   ├── save-benchmarks.csv
│   │   ├── save-benchmarks.html
│   │   ├── save-benchmarks.json
│   │   ├── save-benchmarks.jsonl
│   │   ├── save-benchmarks.md
│   │   ├── save-benchmarks.npy
│   │   ├── save-benchmarks.rst
│   │   ├── save-benchmarks.txt
│   │   ├── save-geojson.geojson
│   │   ├── save-json.csv
│   │   ├── save-json.html
│   │   ├── save-json.json
│   │   ├── save-json.jsonl
│   │   ├── save-json.md
│   │   ├── save-json.tsv
│   │   ├── save-json.txt
│   │   ├── save-jsonla.jsonla
│   │   ├── save-usv.usv
│   │   ├── setcol_fake.tsv
│   │   ├── setcol_precision_less.tsv
│   │   ├── setcol_precision_more.tsv
│   │   ├── sort-levels.tsv
│   │   ├── sortorder.tsv
│   │   ├── sqlite_withoutrowid.tsv
│   │   ├── stdin-replay.tsv
│   │   ├── stdin.tsv
│   │   ├── sum-freq-table.tsv
│   │   ├── test_quartiles.tsv
│   │   ├── transform-cols.tsv
│   │   ├── type-customdate.tsv
│   │   ├── unfurl-dict.tsv
│   │   ├── unfurl-empty-false.tsv
│   │   ├── unfurl-empty.tsv
│   │   ├── unfurl-list.tsv
│   │   ├── xlsx-color-cells.tsv
│   │   ├── xlsx-empty-cell.tsv
│   │   ├── xlsx-header.tsv
│   │   └── xlsx-merged-cells.tsv
│   ├── graph-cursor-nosave.vd
│   ├── graph-sincos-nosave.vdj
│   ├── graphpr-nosave.vd
│   ├── hide-uniform.vdj
│   ├── histogram.vd
│   ├── import-python.vd
│   ├── inner-join.vd
│   ├── invalid_unicode_sqlite.vd
│   ├── issue1308.vdx
│   ├── issue1346.html
│   ├── issue1346.vdx
│   ├── issue1377.vdj
│   ├── issue1377b.vdj
│   ├── issue2015.vdj
│   ├── issue2190.vdj
│   ├── issue2225-nosave.vdx
│   ├── issue2227.html
│   ├── issue2227.vdx
│   ├── issue2316.vd
│   ├── issue2476.vdj
│   ├── issue2524-curcol.vdj
│   ├── issue2890-parquet-addrow-nosave.vdx
│   ├── issue2901-defer-edit-nosave.vdx
│   ├── issue3015-nosave.vdx
│   ├── issue3022-nosave.vdx
│   ├── issue350.json
│   ├── issue350.vdj
│   ├── issue655.vdx
│   ├── issue733.vd
│   ├── issue964-inner-join.vd
│   ├── issue964a.csv
│   ├── issue964b.csv
│   ├── join-cols-single-sheet.vd
│   ├── join-concat.vdj
│   ├── join-different-types.vd
│   ├── join-extend-mult.vd
│   ├── join-merge-1.jsonl
│   ├── join-merge-2.jsonl
│   ├── join-merge.vd
│   ├── join-non-unique-cols.vd
│   ├── joining_error.xlsx
│   ├── joining_error_interesting_records.csv
│   ├── known-broken.vdx
│   ├── listofdictobj.vd
│   ├── load-2d-matrix.vdj
│   ├── load-conllu.vdj
│   ├── load-dir.vd
│   ├── load-fec.vdj
│   ├── load-fixed-header0.vdx
│   ├── load-fixed-leadingspaces.vdx
│   ├── load-fixed.vd
│   ├── load-grep-json.vd
│   ├── load-grep-standard.vd
│   ├── load-h5.vd
│   ├── load-html.vd
│   ├── load-http-flaky.vd
│   ├── load-json.vd
│   ├── load-jsonla.vdj
│   ├── load-lsv.vd
│   ├── load-msgpack.vd
│   ├── load-msgpackz.vd
│   ├── load-numpy.vdj
│   ├── load-ods.vd
│   ├── load-pandas-2.vd
│   ├── load-pandas-3.vd
│   ├── load-pandas.vd
│   ├── load-parquet.vd
│   ├── load-png.vdj
│   ├── load-sqlite-view.vd
│   ├── load-sqlite.vd
│   ├── load-stata.vd
│   ├── load-toml.vd
│   ├── load-usv.vd
│   ├── load-xlsx.vd
│   ├── load-xml.vdj
│   ├── load-xpt-n311.vdj
│   ├── load-yaml.vd
│   ├── load-zip.vd
│   ├── load_npy.vdj
│   ├── long-title-xlsx-nosave.vd
│   ├── macros/
│   │   ├── golden/
│   │   │   └── test_macro.tsv
│   │   └── test_macro.vd
│   ├── melt-error.vd
│   ├── messenger-nosave.vd
│   ├── monthly-revenue.vd
│   ├── numeric-cols.vdj
│   ├── numeric-names.vd
│   ├── numeric_binning.vd
│   ├── outer-join-1.vd
│   ├── outer-join-2.vd
│   ├── output/
│   │   └── .gitignore
│   ├── pandas_loader_dup_selected.vd
│   ├── petsdiet.vd
│   ├── pivot-error.vd
│   ├── pivot-noaggr.vd
│   ├── pivot.vdj
│   ├── pr2302.vdj
│   ├── pr2308.vdj
│   ├── pr2372.tsv
│   ├── pr2372.vdj
│   ├── pr2400.vdj
│   ├── pr2614.vdj
│   ├── pr2647.vdj
│   ├── pr2688a.vdj
│   ├── pr2688b.vdj
│   ├── pr2815.vdj
│   ├── pr2855.vdj
│   ├── prefer-visible-col.vd
│   ├── pull2140.vdj
│   ├── quantum-sum-manual.vdj
│   ├── quit-perf.vdj
│   ├── rank-sheetrank-sorted-cols.vdj
│   ├── record-aggr.vd
│   ├── remove-errors.vd
│   ├── rows-select-expr.vd
│   ├── save-benchmarks.vd
│   ├── save-geojson.vd
│   ├── save-json.vd
│   ├── save-jsonla.vdj
│   ├── save-usv.vd
│   ├── setcol_fake.vdj
│   ├── setcol_precision_less.vdj
│   ├── setcol_precision_more.vdj
│   ├── small.json
│   ├── sort-levels.tsv
│   ├── sort-levels.vd
│   ├── sortorder.vdj
│   ├── sqlite_withoutrowid.vd
│   ├── sum-freq-table.vd
│   ├── test-delimiter.sh
│   ├── test-macros.sh
│   ├── test-perf.sh
│   ├── test-pytest.sh
│   ├── test-roundtrip.sh
│   ├── test-smoke.sh
│   ├── test-startpos.sh
│   ├── test-startup-time.sh
│   ├── test-stdin-replay.sh
│   ├── test-stdin.sh
│   ├── test-vdx.sh
│   ├── test-zsh-syntax.sh
│   ├── test_quartiles.vd
│   ├── testenv.sh
│   ├── transform-cols.vd
│   ├── type-customdate.vdj
│   ├── unfurl-dict.vd
│   ├── unfurl-empty-false.vd
│   ├── unfurl-empty.jsonl
│   ├── unfurl-empty.vd
│   ├── unfurl-list.vd
│   ├── xdg/
│   │   └── data/
│   │       └── visidata/
│   │           ├── 1.vdj
│   │           └── macros.jsonl
│   ├── xlsx-color-cells.vd
│   ├── xlsx-empty-cell.vd
│   ├── xlsx-header.vd
│   └── xlsx-merged-cells.vd
└── visidata/
    ├── __init__.py
    ├── __main__.py
    ├── _input.py
    ├── _open.py
    ├── _types.py
    ├── _urlcache.py
    ├── aggregators.py
    ├── apps/
    │   ├── __init__.py
    │   ├── galcon/
    │   │   ├── Dockerfile.galcon-client
    │   │   ├── Dockerfile.galcon-server
    │   │   ├── README.md
    │   │   ├── galcon-server.py
    │   │   ├── galcon.py
    │   │   ├── requirements.txt
    │   │   └── setup.py
    │   ├── vdsql/
    │   │   ├── .gitignore
    │   │   ├── CHANGELOG.md
    │   │   ├── MANIFEST.in
    │   │   ├── README.md
    │   │   ├── __about__.py
    │   │   ├── __init__.py
    │   │   ├── __main__.py
    │   │   ├── _ibis.py
    │   │   ├── bigquery.py
    │   │   ├── clickhouse.py
    │   │   ├── demos/
    │   │   │   └── clickhouse-demo.vdx
    │   │   ├── requirements-extra.txt
    │   │   ├── requirements.txt
    │   │   ├── setup.py
    │   │   ├── snowflake.py
    │   │   ├── test.sh
    │   │   ├── tests/
    │   │   │   ├── dup-limit.vdj
    │   │   │   ├── freq-open-selected.vdj
    │   │   │   ├── freq.vdj
    │   │   │   ├── golden/
    │   │   │   │   ├── dup-limit.jsonl
    │   │   │   │   ├── freq-open-selected.jsonl
    │   │   │   │   ├── freq.jsonl
    │   │   │   │   ├── scoop.jsonl
    │   │   │   │   ├── select-col-regex.jsonl
    │   │   │   │   ├── select-expr-cast-type.tsv
    │   │   │   │   ├── select-expr.jsonl
    │   │   │   │   ├── select-row-dup.jsonl
    │   │   │   │   ├── toggle.jsonl
    │   │   │   │   ├── unselect-regex.tsv
    │   │   │   │   └── unselect.jsonl
    │   │   │   ├── scoop.vdj
    │   │   │   ├── select-col-regex.vdj
    │   │   │   ├── select-expr-cast-type.vdj
    │   │   │   ├── select-expr.vdj
    │   │   │   ├── select-row-dup.vdj
    │   │   │   ├── toggle.vdj
    │   │   │   ├── unselect-regex.vdj
    │   │   │   └── unselect.vdj
    │   │   └── vdsql
    │   └── vgit/
    │       ├── CHANGELOG.md
    │       ├── README.md
    │       ├── USECASES.md
    │       ├── __init__.py
    │       ├── __main__.py
    │       ├── abort.py
    │       ├── blame.py
    │       ├── branch.py
    │       ├── config.py
    │       ├── diff.py
    │       ├── gitsheet.py
    │       ├── grep.py
    │       ├── log.py
    │       ├── main.py
    │       ├── remote.py
    │       ├── repos.py
    │       ├── setup.py
    │       ├── stash.py
    │       ├── status.py
    │       ├── statusbar.py
    │       ├── tests/
    │       │   ├── git_branch_test.vdx
    │       │   └── git_remote_test.vdx
    │       ├── vgit
    │       └── vgit-guide.md
    ├── basesheet.py
    ├── bezier.py
    ├── canvas.py
    ├── canvas_text.py
    ├── choose.py
    ├── clean_names.py
    ├── clipboard.py
    ├── cliptext.py
    ├── cmdlog.py
    ├── color.py
    ├── column.py
    ├── ddw/
    │   ├── input.ddw
    │   └── regex.ddw
    ├── ddwplay.py
    ├── deprecated.py
    ├── desktop/
    │   ├── org.visidata.VisiData.metainfo.xml
    │   └── visidata.desktop
    ├── editor.py
    ├── errors.py
    ├── experimental/
    │   ├── __init__.py
    │   ├── daw/
    │   │   ├── CLAUDE.md
    │   │   ├── README.md
    │   │   ├── TODO.md
    │   │   ├── __init__.py
    │   │   ├── merge_transcripts.py
    │   │   ├── mpv.py
    │   │   ├── vdaw.py
    │   │   └── xmd2json.py
    │   ├── diff_sheet.py
    │   ├── digit_autoedit.py
    │   ├── gdrive.py
    │   ├── google.py
    │   ├── gsheets.py
    │   ├── helloworld.py
    │   ├── live_search.py
    │   ├── liveupdate.py
    │   ├── llm.py
    │   ├── mark.py
    │   ├── noahs_tapestry/
    │   │   ├── __init__.py
    │   │   ├── clues.json
    │   │   ├── flame.ddw
    │   │   ├── menorah.ddw
    │   │   ├── puzzle0.md
    │   │   ├── puzzle1.md
    │   │   ├── puzzle2.md
    │   │   ├── puzzle3.md
    │   │   ├── puzzle4.md
    │   │   ├── puzzle5.md
    │   │   ├── puzzle6.md
    │   │   ├── puzzle7.md
    │   │   ├── puzzle8.md
    │   │   ├── solutions.json
    │   │   ├── tapestry.ddw
    │   │   └── tapestry.py
    │   ├── rownum.py
    │   ├── slide_cells.py
    │   ├── sort_selected.py
    │   └── vimcompat.py
    ├── expr.py
    ├── extensible.py
    ├── features/
    │   ├── __init__.py
    │   ├── addcol_audiometadata.py
    │   ├── addcol_histogram.py
    │   ├── canvas_save_svg.py
    │   ├── change_precision.py
    │   ├── cmdpalette.py
    │   ├── colorbrewer.py
    │   ├── colorsheet.py
    │   ├── command_server.py
    │   ├── currency_to_usd.py
    │   ├── customdate.py
    │   ├── dedupe.py
    │   ├── describe.py
    │   ├── expand_cols.py
    │   ├── fill.py
    │   ├── freeze.py
    │   ├── go_col.py
    │   ├── graph_seaborn.py
    │   ├── graph_zoom_y.py
    │   ├── hint_types.py
    │   ├── hlsearch.py
    │   ├── icon.py
    │   ├── incr.py
    │   ├── join.py
    │   ├── known_cols.py
    │   ├── layout.py
    │   ├── melt.py
    │   ├── normcol.py
    │   ├── open_config.py
    │   ├── open_syspaste.py
    │   ├── ping.py
    │   ├── procmgr.py
    │   ├── pypkg.py
    │   ├── random_sample.py
    │   ├── rank.py
    │   ├── regex.py
    │   ├── reload_every.py
    │   ├── rename_col_cascade.py
    │   ├── repeat.py
    │   ├── repl.py
    │   ├── replay_bulk.py
    │   ├── scroll_context.py
    │   ├── select_equal_selected.py
    │   ├── setcol_fake.py
    │   ├── slide.py
    │   ├── sparkline.py
    │   ├── status_source.py
    │   ├── sysedit.py
    │   ├── sysopen_mailcap.py
    │   ├── term_extras.py
    │   ├── transpose.py
    │   ├── type_ipaddr.py
    │   ├── type_url.py
    │   ├── unfurl.py
    │   └── window.py
    ├── form.py
    ├── freqtbl.py
    ├── fuzzymatch.py
    ├── graph.py
    ├── guide.py
    ├── guides/
    │   ├── AggregatorsSheet.md
    │   ├── ClipboardGuide.md
    │   ├── ColumnsGuide.md
    │   ├── CommandsSheet.md
    │   ├── DirSheet.md
    │   ├── ErrorsSheet.md
    │   ├── FrequencyTable.md
    │   ├── GrepSheet.md
    │   ├── JsonSheet.md
    │   ├── MacrosSheet.md
    │   ├── MeltGuide.md
    │   ├── MemorySheet.md
    │   ├── MenuGuide.md
    │   ├── ModifyGuide.md
    │   ├── MovementGuide.md
    │   ├── PivotGuide.md
    │   ├── RegexGuide.md
    │   ├── SelectionGuide.md
    │   ├── SlideGuide.md
    │   ├── SortGuide.md
    │   ├── SplitpaneGuide.md
    │   ├── TypesSheet.md
    │   ├── WindowFunctionGuide.md
    │   └── XsvGuide.md
    ├── help.py
    ├── hint.py
    ├── indexsheet.py
    ├── input_history.py
    ├── interface.py
    ├── keys.py
    ├── loaders/
    │   ├── __init__.py
    │   ├── _pandas.py
    │   ├── api_airtable.py
    │   ├── api_matrix.py
    │   ├── api_reddit.py
    │   ├── api_zulip.py
    │   ├── archive.py
    │   ├── arrow.py
    │   ├── claude.py
    │   ├── conll.py
    │   ├── csv.py
    │   ├── eml.py
    │   ├── f5log.py
    │   ├── fec.py
    │   ├── fixed_width.py
    │   ├── frictionless.py
    │   ├── geojson.py
    │   ├── google.py
    │   ├── graphviz.py
    │   ├── grep.py
    │   ├── hdf5.py
    │   ├── html.py
    │   ├── http.py
    │   ├── imap.py
    │   ├── jrnl.py
    │   ├── json.py
    │   ├── jsonla.py
    │   ├── lsv.py
    │   ├── mailbox.py
    │   ├── markdown.py
    │   ├── mbtiles.py
    │   ├── msgpack.py
    │   ├── mysql.py
    │   ├── npy.py
    │   ├── odf.py
    │   ├── orgmode.py
    │   ├── pandas_freqtbl.py
    │   ├── parquet.py
    │   ├── pcap.py
    │   ├── pdf.py
    │   ├── png.py
    │   ├── postgres.py
    │   ├── psv.py
    │   ├── rec.py
    │   ├── s3.py
    │   ├── sas.py
    │   ├── scrape.py
    │   ├── shp.py
    │   ├── spss.py
    │   ├── sqlite.py
    │   ├── texttables.py
    │   ├── toml.py
    │   ├── tsv.py
    │   ├── ttf.py
    │   ├── unzip_http.py
    │   ├── usv.py
    │   ├── vcf.py
    │   ├── vds.py
    │   ├── vdx.py
    │   ├── xlsb.py
    │   ├── xlsx.py
    │   ├── xml.py
    │   ├── xword.py
    │   └── yaml.py
    ├── macos.py
    ├── macros.py
    ├── main.py
    ├── mainloop.py
    ├── man/
    │   ├── parse_options.py
    │   └── vd.inc
    ├── memory.py
    ├── menu.py
    ├── metasheets.py
    ├── modify.py
    ├── motd.py
    ├── mouse.py
    ├── movement.py
    ├── optionssheet.py
    ├── path.py
    ├── pivot.py
    ├── plugins.py
    ├── pyobj.py
    ├── rename_col.py
    ├── save.py
    ├── search.py
    ├── selection.py
    ├── settings.py
    ├── sheets.py
    ├── shell.py
    ├── sidebar.py
    ├── sort.py
    ├── statusbar.py
    ├── stored_list.py
    ├── tests/
    │   ├── __init__.py
    │   ├── benchmark.csv
    │   ├── conftest.py
    │   ├── sample.tsv
    │   ├── test_cliptext.py
    │   ├── test_commands.py
    │   ├── test_completer.py
    │   ├── test_date.py
    │   ├── test_edittext.py
    │   ├── test_features.py
    │   ├── test_fixed_width.py
    │   ├── test_keystrokes.py
    │   ├── test_menu.py
    │   ├── test_parsepos.py
    │   └── test_path.py
    ├── text_source.py
    ├── textsheet.py
    ├── theme.py
    ├── themes/
    │   ├── __init__.py
    │   ├── adwaita.py
    │   ├── ascii8.py
    │   ├── asciimono.py
    │   └── light.py
    ├── threads.py
    ├── tuiwin.py
    ├── type_currency.py
    ├── type_date.py
    ├── type_floatsi.py
    ├── undo.py
    ├── utils.py
    ├── vdobj.py
    ├── vendor/
    │   ├── __init__.py
    │   └── appdirs.py
    ├── windows.py
    └── wrappers.py
Download .txt
SYMBOL INDEX (2918 symbols across 244 files)

FILE: bin/viewtsv.py
  function open_tsv (line 9) | def open_tsv(vd, p):
  class MinimalTsvSheet (line 13) | class MinimalTsvSheet(Sheet):
    method reload (line 17) | def reload(self):

FILE: dev/visidata-brew.rb
  class Visidata (line 1) | class Visidata < Formula
    method install (line 65) | def install

FILE: dev/zsh-completion.py
  function generate_completion (line 18) | def generate_completion(opt):

FILE: plugins/geocoding.py
  function geocode (line 18) | def geocode(addr):
  function geocode_col (line 26) | def geocode_col(sheet, vcolidx):

FILE: plugins/vtask.py
  function editTask (line 11) | def editTask(task):
  class TodoSheet (line 39) | class TodoSheet(Sheet):
    method newRow (line 66) | def newRow(self, **kwargs):
    method reload (line 69) | def reload(self):
  function isChanged (line 74) | def isChanged(r, key):
  class TaskAnnotationsSheet (line 78) | class TaskAnnotationsSheet(Sheet):
    method reload (line 84) | def reload(self):
  function main_vtask (line 101) | def main_vtask():

FILE: setup.py
  function all_requirements (line 9) | def all_requirements():

FILE: snippets/gender.py
  function gg_detect (line 15) | def gg_detect():
  function gender (line 19) | def gender(first_name):

FILE: snippets/geolocate.py
  function geolocate (line 9) | def geolocate(ip):

FILE: snippets/rank.py
  class RankColumn (line 11) | class RankColumn(Column):
    method __init__ (line 12) | def __init__(self, col, **kwargs):
    method calcValue (line 18) | def calcValue(self, row):
    method resetCache (line 22) | def resetCache(self):

FILE: visidata/__init__.py
  class EscapeException (line 10) | class EscapeException(BaseException):
  function addGlobals (line 15) | def addGlobals(*args, **kwargs):
  function getGlobals (line 25) | def getGlobals():
  function importFeatures (line 138) | def importFeatures():

FILE: visidata/_input.py
  class AcceptInput (line 19) | class AcceptInput(Exception):
  function injectInput (line 27) | def injectInput(vd, x):
  function getCommandInput (line 34) | def getCommandInput(vd):
  function execCommand (line 44) | def execCommand(sheet, longname, *args, **kwargs):
  function acceptThenFunc (line 50) | def acceptThenFunc(*longnames):
  class EnableCursor (line 59) | class EnableCursor:
    method __enter__ (line 60) | def __enter__(self):
    method __exit__ (line 65) | def __exit__(self, exc_type, exc_val, tb):
  function until_get_wch (line 71) | def until_get_wch(scr):
  function splice (line 85) | def splice(v:str, i:int, s:str):
  function drawInputHelp (line 91) | def drawInputHelp(vd, scr):
  function clean_printable (line 102) | def clean_printable(s):
  function delchar (line 107) | def delchar(s, i, remove=1):
  function find_word (line 111) | def find_word(s, a, b, incr):
  class InputWidget (line 130) | class InputWidget:
    method __init__ (line 131) | def __init__(self,
    method editline (line 173) | def editline(self, scr, y, x, w, attr=ColorAttr(), updater=lambda val:...
    method draw (line 191) | def draw(self, scr, y, x, w, attr=ColorAttr(), clear=True):
    method handle_key (line 247) | def handle_key(self, ch:str, scr) -> bool:
    method completion (line 318) | def completion(self, v, i, state_incr):
    method reset_completion (line 337) | def reset_completion(self):
    method prev_history (line 344) | def prev_history(self, v, i):
    method next_history (line 354) | def next_history(self, v, i):
  function editText (line 368) | def editText(vd, y, x, w, attr=ColorAttr(), value='',
  function inputsingle (line 415) | def inputsingle(vd, prompt, record=True):
  function inputMultiple (line 441) | def inputMultiple(vd, updater=lambda val: None, record=True, **kwargs):
  function input (line 539) | def input(vd, prompt, type=None, defaultLast=False, history=[], dy=0, at...
  function confirm (line 609) | def confirm(vd, prompt, exc=EscapeException):
  class CompleteKey (line 624) | class CompleteKey:
    method __init__ (line 625) | def __init__(self, items):
    method __call__ (line 628) | def __call__(self, val, state):
  function editCell (line 634) | def editCell(self, vcolidx=None, rowidx=None, value=None, **kwargs):

FILE: visidata/_open.py
  function inputFilename (line 12) | def inputFilename(vd, prompt, *args, **kwargs):
  function inputPath (line 23) | def inputPath(vd, *args, **kwargs):
  function _completeFilename (line 27) | def _completeFilename(val, state):
  function guessFiletype (line 49) | def guessFiletype(vd, p, *args, funcprefix='guess_'):
  function guess_extension (line 72) | def guess_extension(vd, path):
  function openPath (line 81) | def openPath(vd, p, filetype=None, create=False):
  function openSource (line 149) | def openSource(vd, p, filetype=None, create=False, **kwargs):
  function open_txt (line 180) | def open_txt(vd, p):

FILE: visidata/_types.py
  function anytype (line 34) | def anytype(r=None):
  function numericFormatter (line 41) | def numericFormatter(vd, fmtstr, typedval):
  function numericType (line 53) | def numericType(vd, icon='', fmtstr='', formatter=vd.numericFormatter):
  class VisiDataType (line 62) | class VisiDataType:
    method __init__ (line 64) | def __init__(self, typetype=None, icon=None, fmtstr='', formatter=vd.n...
  function addType (line 73) | def addType(vd, typetype=None, icon=None, fmtstr='', formatter=vd.numeri...
  function getType (line 95) | def getType(vd, typetype):
  function numtype (line 100) | def numtype(r=None):
  function isNumeric (line 116) | def isNumeric(vd, col):
  function deduceType (line 119) | def deduceType(v):
  function floatlocale (line 127) | def floatlocale(*args):
  class vlen (line 136) | class vlen(int):
    method __new__ (line 137) | def __new__(cls, v=0):
    method __len__ (line 143) | def __len__(self):

FILE: visidata/_urlcache.py
  function urlcache (line 9) | def urlcache(vd, url, days=1, text=True, headers={}):
  function enable_requests_cache (line 40) | def enable_requests_cache(vd):

FILE: visidata/aggregators.py
  function getValueRows (line 30) | def getValueRows(self, rows):
  function getValues (line 44) | def getValues(self, rows):
  function aggregators_get (line 56) | def aggregators_get(col):
  function aggregators_set (line 65) | def aggregators_set(col, aggs):
  class Aggregator (line 82) | class Aggregator:
    method __init__ (line 83) | def __init__(self, name, type, funcValues=None, helpstr=''):
    method aggregate (line 90) | def aggregate(self, col, rows):  # wrap builtins so they can have a .type
  class ListAggregator (line 100) | class ListAggregator(Aggregator):
    method __init__ (line 106) | def __init__(self, name, type, helpstr='', listtype=None):
    method aggregate (line 112) | def aggregate(self, col, rows) -> list:
    method aggregate_list (line 120) | def aggregate_list(self, col, row_group) -> list:
  function aggregator (line 130) | def aggregator(vd, name, funcValues, helpstr='', *, type=None):
  function aggregator_list (line 137) | def aggregator_list(vd, name, helpstr='', type=anytype, listtype=anytype):
  function mean (line 147) | def mean(vals):
  function vsum (line 153) | def vsum(vals):
  function stdev (line 157) | def stdev(vals):
  function _percentile (line 169) | def _percentile(N, percent, key=lambda x:x):
  class PercentileAggregator (line 192) | class PercentileAggregator(Aggregator):
    method __init__ (line 193) | def __init__(self, pct, helpstr=''):
    method aggregate (line 197) | def aggregate(self, col, rows):
  function quantiles (line 201) | def quantiles(q, helpstr):
  function aggregate_groups (line 205) | def aggregate_groups(sheet, col, rows, aggr) -> list:
  class KeyFindingAggregator (line 269) | class KeyFindingAggregator(Aggregator):
    method __init__ (line 277) | def __init__(self, aggr_func, *args, **kwargs):
    method aggregate (line 281) | def aggregate(self, col, rows):
  function addAggregators (line 303) | def addAggregators(sheet, cols, aggrnames):
  function aggname (line 316) | def aggname(col, agg):
  function aggregateTotal (line 322) | def aggregateTotal(col, agg):
  function _aggregateTotalAsync (line 331) | def _aggregateTotalAsync(col, agg):
  function memo_aggregate (line 337) | def memo_aggregate(col, agg_choices, rows):
  function aggregator_choices (line 354) | def aggregator_choices(vd):
  function chooseAggregators (line 363) | def chooseAggregators(vd, prompt = 'choose aggregators: '):
  function addcol_aggregate (line 394) | def addcol_aggregate(sheet, col, aggrnames):

FILE: visidata/apps/galcon/galcon-server.py
  function error (line 18) | def error(s):
  class OptionsObject (line 22) | class OptionsObject:
    method __init__ (line 24) | def __init__(self, d):
    method __getattr__ (line 26) | def __getattr__(self, k):
    method __setitem__ (line 28) | def __setitem__(self, k, v):
  class Game (line 33) | class Game:
    method __init__ (line 34) | def __init__(self):
    method generate_planets (line 53) | def generate_planets(self):
    method notify (line 75) | def notify(self, eventstr):
    method start_game (line 78) | def start_game(self):
    method end_turn (line 81) | def end_turn(self):
    method next_turn (line 164) | def next_turn(self):
    method GET_scores (line 177) | def GET_scores(self, pl, **kwargs):
    method GET_player_quit (line 193) | def GET_player_quit(self, pl, **kwargs):
    method started (line 207) | def started(self):
    method POST_options (line 210) | def POST_options(self, pl, **kwargs):
    method GET_options (line 219) | def GET_options(self, pl, **kwargs):
    method GET_set_option (line 222) | def GET_set_option(self, pl, option='', value=''):
    method GET_regen_map (line 231) | def GET_regen_map(self, pl, **kwargs):
    method GET_gamestate (line 237) | def GET_gamestate(self, pl, **kwargs):
    method POST_auth (line 247) | def POST_auth(self, pl, **kwargs):
    method GET_join (line 250) | def GET_join(self, pl, **kwargs):
    method GET_ready (line 263) | def GET_ready(self, pl, **kwargs):
    method GET_players (line 277) | def GET_players(self, pl, **kwargs):
    method GET_planets (line 280) | def GET_planets(self, pl, **kwargs):
    method GET_deployments (line 283) | def GET_deployments(self, pl, **kwargs):
    method GET_events (line 286) | def GET_events(self, pl):
    method predeploy (line 289) | def predeploy(self, launch_player, launch_planet_name=None, dest_plane...
    method GET_deploy (line 310) | def GET_deploy(self, launch_player, launch_planet_name=None, dest_plan...
    method GET_validate_deploy (line 327) | def GET_validate_deploy(self, launch_player, launch_planet_name=None, ...
    method GET_end_turn (line 332) | def GET_end_turn(self, pl):
    method distance (line 343) | def distance(self, here, dest):
  class Player (line 351) | class Player:
    method __init__ (line 352) | def __init__(self, name, md5_password, sessionid):
    method as_dict (line 359) | def as_dict(self):
    method __str__ (line 365) | def __str__(self):
  class Planet (line 369) | class Planet:
    method __init__ (line 370) | def __init__(self, name, x, y, prod=0, killpct=0, owner=None):
    method xy (line 380) | def xy(self):
    method __str__ (line 383) | def __str__(self):
    method as_dict (line 386) | def as_dict(self):
  class Deployment (line 405) | class Deployment:
    method __init__ (line 406) | def __init__(self, launch_player, launch_turn, launch_planet, dest_pla...
    method as_dict (line 416) | def as_dict(self):
  class Event (line 429) | class Event:
    method __init__ (line 430) | def __init__(self, turn_num, eventstr):
    method as_dict (line 434) | def as_dict(self):
  class HTTPException (line 443) | class HTTPException(Exception):
    method __init__ (line 444) | def __init__(self, errcode, text):
  class WSIServer (line 449) | class WSIServer(http.server.HTTPServer):
    method __init__ (line 450) | def __init__(self, *args, **kwargs):
  class WSIHandler (line 457) | class WSIHandler(http.server.BaseHTTPRequestHandler):
    method generic_handler (line 458) | def generic_handler(self, reqtype, path, data):
    method do_GET (line 516) | def do_GET(self):
    method do_POST (line 520) | def do_POST(self):
  function generate_map_random (line 526) | def generate_map_random(width, height, num_planets, distancefunc):
  function generate_map_rclogo_fixed (line 542) | def generate_map_rclogo_fixed(width, height, num_planets, distancefunc):
  function generate_map_rclogo_var (line 583) | def generate_map_rclogo_var(width, height, num_planets, distancefunc):
  function main (line 649) | def main():

FILE: visidata/apps/galcon/galcon.py
  function openhttp_galcon (line 30) | def openhttp_galcon(vd, p):
  class GalconSheet (line 39) | class GalconSheet(Sheet):
  class WSIClient (line 55) | class WSIClient:
    method __init__ (line 56) | def __init__(self, url):
    method submit_turn (line 72) | def submit_turn(self):
    method current_turn (line 89) | def current_turn(self):
    method rightStatus (line 96) | def rightStatus(self, sheet):
    method login (line 116) | def login(self):  # before curses init
    method get (line 134) | def get(self, path, **kwargs):
    method player_quit (line 147) | def player_quit(self):
    method add_deployment (line 154) | def add_deployment(self, sources, dest, nships):
    method refresh_everything (line 169) | def refresh_everything(self):
  class PlayersSheet (line 197) | class PlayersSheet(GalconSheet):
    method loader (line 206) | def loader(self):
    method get_player_color (line 213) | def get_player_color(self, playername):
  function distance_turns (line 220) | def distance_turns(pl1, pl2):
  class PlanetsSheet (line 225) | class PlanetsSheet(GalconSheet):
    method reload (line 243) | def reload(self):
  class QueuedDeploymentsSheet (line 256) | class QueuedDeploymentsSheet(GalconSheet):
    method colorIncomplete (line 270) | def colorIncomplete(self, col, row, value):
    method reload (line 275) | def reload(self):
  class HistoricalDeploymentsSheet (line 279) | class HistoricalDeploymentsSheet(GalconSheet):
    method reload (line 294) | def reload(self):
  class EventsSheet (line 300) | class EventsSheet(GalconSheet):
    method reload (line 307) | def reload(self):
  function CellColor (line 319) | def CellColor(prec, color, func):
  class MapSheet (line 323) | class MapSheet(GalconSheet):
    method title (line 333) | def title(self):
    method title (line 337) | def title(self, value):
    method colorSpace (line 341) | def colorSpace(sheet,col,row,value):
    method cycle_info (line 349) | def cycle_info(self):
    method reload (line 353) | def reload(self):
  class GameOptionsSheet (line 386) | class GameOptionsSheet(GalconSheet):
    method reload (line 392) | def reload(self):

FILE: visidata/apps/vdsql/__main__.py
  function main (line 3) | def main():

FILE: visidata/apps/vdsql/_ibis.py
  function vdtype_to_ibis_type (line 16) | def vdtype_to_ibis_type(t):
  function dtype_to_vdtype (line 26) | def dtype_to_vdtype(dtype):
  function configure_ibis (line 48) | def configure_ibis(vd):
  function open_vdsql (line 63) | def open_vdsql(vd, p, filetype=None):
  class IbisConnectionPool (line 81) | class IbisConnectionPool:
    method __init__ (line 82) | def __init__(self, source, pool=None, total=0):
    method __copy__ (line 87) | def __copy__(self):
    method get_conn (line 91) | def get_conn(self):
  class IbisTableIndexSheet (line 98) | class IbisTableIndexSheet(IndexSheet):
    method con (line 102) | def con(self):
    method rawSql (line 105) | def rawSql(self, qstr):
    method iterload (line 113) | def iterload(self):
  class IbisColumn (line 143) | class IbisColumn(ItemColumn):
    method ibis_type (line 145) | def ibis_type(self):
    method memo_aggregate (line 149) | def memo_aggregate(self, agg, rows):
    method expand (line 164) | def expand(self, rows):
    method expand_struct (line 167) | def expand_struct(self, rows):
  class LazyIbisColMap (line 185) | class LazyIbisColMap:
    method __init__ (line 186) | def __init__(self, sheet, q):
    method __getitem__ (line 191) | def __getitem__(self, k):
  class IbisTableSheet (line 196) | class IbisTableSheet(Sheet):
    method con (line 198) | def con(self):
    method help_sidebars (line 202) | def help_sidebars(self):
    method choose_sidebar (line 235) | def choose_sidebar(self):
    method curcol_sql (line 248) | def curcol_sql(self):
    method ibis_expr_to_sql (line 253) | def ibis_expr_to_sql(self, expr, fragment=False):
    method sidebar (line 259) | def sidebar(self) -> str:
    method ibis_locals (line 267) | def ibis_locals(self):
    method select_row (line 270) | def select_row(self, row):
    method stoggle_row (line 275) | def stoggle_row(self, row):
    method unselect_row (line 278) | def unselect_row(self, row):
    method matchRowKeyExpr (line 282) | def matchRowKeyExpr(self, row):
    method ibis_current_expr (line 292) | def ibis_current_expr(self):
    method str_current_expr (line 296) | def str_current_expr(self):
    method str_pending_expr (line 300) | def str_pending_expr(self):
    method get_current_expr (line 303) | def get_current_expr(self, typed=False):
    method ibis_filter (line 319) | def ibis_filter(self):
    method pending_expr (line 327) | def pending_expr(self):
    method ibisCompileExpr (line 344) | def ibisCompileExpr(self, expr, q):
    method evalIbisExpr (line 350) | def evalIbisExpr(self, expr):
    method base_sql (line 354) | def base_sql(self):
    method pending_sql (line 358) | def pending_sql(self):
    method sqlize (line 361) | def sqlize(self, expr):
    method substrait (line 367) | def substrait(self):
    method withRowcount (line 372) | def withRowcount(self, q):
    method beforeLoad (line 378) | def beforeLoad(self):
    method baseQuery (line 382) | def baseQuery(self, con):
    method fqtblname (line 388) | def fqtblname(self, con) -> str:
    method iterload (line 394) | def iterload(self):
    method reloadColumns (line 406) | def reloadColumns(self, expr, start=1):
    method countSelectedRows (line 425) | def countSelectedRows(self):
    method countRows (line 429) | def countRows(self):
    method groupBy (line 436) | def groupBy(self, groupByCols):
    method unfurl_col (line 473) | def unfurl_col(self, col):
    method openJoin (line 480) | def openJoin(self, others, jointype=''):
  function ibis_col (line 516) | def ibis_col(col):
  function get_ibis_col (line 521) | def get_ibis_col(col, query:'ibis.Expr', typed=False) -> 'ibis.Expr':
  function ibis_aggrs (line 555) | def ibis_aggrs(col):
  function ibis_aggr (line 560) | def ibis_aggr(col, aggname):
  function stoggle_rows (line 582) | def stoggle_rows(sheet):
  function clearSelected (line 588) | def clearSelected(sheet):
  function addUndoSelection (line 595) | def addUndoSelection(sheet):
  function select_equal_cell (line 601) | def select_equal_cell(sheet, col, typedval):
  function select_col_regex (line 614) | def select_col_regex(sheet, col, regex):
  function select_expr (line 620) | def select_expr(sheet, expr):
  function addcol_split (line 626) | def addcol_split(sheet, col, delim):
  function addcol_subst (line 638) | def addcol_subst(sheet, col, before='', after=''):
  function addcol_cast (line 648) | def addcol_cast(sheet, col):
  function notimpl (line 663) | def notimpl(sheet):
  function dup_selected (line 692) | def dup_selected(sheet):
  function incrementName (line 700) | def incrementName(sheet):
  function dup_limit (line 709) | def dup_limit(sheet, limit:int):
  function rawSql (line 717) | def rawSql(sheet, qstr):
  class IbisFreqTable (line 725) | class IbisFreqTable(IbisTableSheet):
    method freqExpr (line 726) | def freqExpr(self, row):
    method selectRow (line 733) | def selectRow(self, row):
    method openRow (line 738) | def openRow(self, row):
    method openRows (line 744) | def openRows(self, rows):

FILE: visidata/apps/vdsql/bigquery.py
  function openurl_bigquery (line 15) | def openurl_bigquery(vd, p, filetype=None):
  function configure_bigquery (line 24) | def configure_bigquery(vd):
  class BigqueryDatabaseIndexSheet (line 33) | class BigqueryDatabaseIndexSheet(Sheet):
    method con (line 45) | def con(self):
    method iterload (line 51) | def iterload(self):
    method openRow (line 54) | def openRow(self, row):

FILE: visidata/apps/vdsql/clickhouse.py
  function openurl_clickhouse (line 9) | def openurl_clickhouse(vd, p, filetype=None):
  class ClickhouseSheet (line 17) | class ClickhouseSheet(IbisTableSheet):
    method countRows (line 19) | def countRows(self):
    method iterload (line 24) | def iterload(self):

FILE: visidata/apps/vdsql/setup.py
  function readme (line 10) | def readme():
  function requirements (line 14) | def requirements():
  function requirements_extra (line 17) | def requirements_extra():

FILE: visidata/apps/vdsql/snowflake.py
  function openurl_snowflake (line 9) | def openurl_snowflake(vd, p, filetype=None):
  class SnowflakeSheet (line 15) | class SnowflakeSheet(IbisTableSheet):
    method countRows (line 17) | def countRows(self):
    method executeSql (line 23) | def executeSql(self, sql):
    method iterload (line 44) | def iterload(self):
    method cancelQuery (line 57) | def cancelQuery(self, qid):

FILE: visidata/apps/vgit/abort.py
  function abortWhatever (line 6) | def abortWhatever(sheet):

FILE: visidata/apps/vgit/blame.py
  function git_blame (line 7) | def git_blame(vd, gitpath, args, **kwargs):
  class FormatColumn (line 13) | class FormatColumn(Column):
    method calcValue (line 14) | def calcValue(self, row):
  class GitBlame (line 21) | class GitBlame(GitSheet):
    method iterload (line 38) | def iterload(self):

FILE: visidata/apps/vgit/branch.py
  function git_branch (line 12) | def git_branch(vd, p, args):
  function _remove_prefix (line 20) | def _remove_prefix(text, prefix):
  class GitBranchNameColumn (line 26) | class GitBranchNameColumn(WritableColumn):
    method calcValue (line 27) | def calcValue(self, row):
    method putValue (line 30) | def putValue(self, row, val):
  class GitBranch (line 34) | class GitBranch(GitSheet):
    method iterload (line 63) | def iterload(self):
    method commitAddRow (line 107) | def commitAddRow(self, row):
    method commitDeleteRow (line 110) | def commitDeleteRow(self, row):
  function gitBranchStatuses (line 115) | def gitBranchStatuses(sheet):

FILE: visidata/apps/vgit/config.py
  function git_config (line 11) | def git_config(vd, p, args):
  function batched (line 17) | def batched(iterable, n=1):
  class GitConfigColumn (line 26) | class GitConfigColumn(Column):
    method calcValue (line 27) | def calcValue(self, row):
    method putValue (line 30) | def putValue(self, row, val):
  class GitConfig (line 39) | class GitConfig(GitSheet):
    method iterload (line 54) | def iterload(self):
    method commitDeleteRow (line 79) | def commitDeleteRow(self, row):
    method commitAddRow (line 84) | def commitAddRow(self, row):

FILE: visidata/apps/vgit/diff.py
  function git_diff (line 11) | def git_diff(vd, p, args):
  function _parseStartCount (line 14) | def _parseStartCount(s):
  class GitDiffSheet (line 22) | class GitDiffSheet(GitSheet):
    method hunkCount (line 36) | def hunkCount(self, row):
    method cursorLines (line 40) | def cursorLines(self):
    method iterload (line 52) | def iterload(self):
    method openRow (line 94) | def openRow(self, row):
  class HunkViewer (line 98) | class HunkViewer(GitSheet):
    method draw (line 109) | def draw(self, scr):
    method iterload (line 114) | def iterload(self):
  function git_apply (line 148) | def git_apply(sheet, row, *args):

FILE: visidata/apps/vgit/gitsheet.py
  class GitSheet (line 6) | class GitSheet(Sheet):
    method gitargstr (line 8) | def gitargstr(self):
    method git (line 11) | def git(self, subcmd, *args, **kwargs):
    method loggit (line 20) | def loggit(self, subcmd, *args, **kwargs):
    method git_all (line 29) | def git_all(self, *args, **kwargs):
    method git_lines (line 45) | def git_lines(self, subcmd, *args, **kwargs):
    method git_iter (line 69) | def git_iter(self, subcmd, *args, sep='\0', **kwargs):
    method modifyGit (line 109) | def modifyGit(self, *args, **kwargs):
    method gitRootSheet (line 121) | def gitRootSheet(self):
    method iterload (line 126) | def iterload(self):
  function gitRootPath (line 132) | def gitRootPath(self):
  function branch (line 148) | def branch(self):

FILE: visidata/apps/vgit/grep.py
  function git_grep (line 7) | def git_grep(vd, p, args):
  class GitGrep (line 11) | class GitGrep(GitSheet):
    method iterload (line 26) | def iterload(self):

FILE: visidata/apps/vgit/log.py
  function git_log (line 9) | def git_log(vd, p, *args):
  class GitLogSheet (line 13) | class GitLogSheet(GitSheet):
    method __init__ (line 34) | def __init__(self, *args, **kwargs):
    method inRemoteBranch (line 38) | def inRemoteBranch(self, commitid):
    method iterload (line 41) | def iterload(self):
    method openRow (line 54) | def openRow(self, row):
    method commit (line 59) | def commit(self, path, adds, mods, dels):

FILE: visidata/apps/vgit/main.py
  function vgit_cli (line 14) | def vgit_cli():

FILE: visidata/apps/vgit/remote.py
  function git_remote (line 6) | def git_remote(vd, p, args):
  class GitRemotes (line 11) | class GitRemotes(GitSheet):
    method set_remote (line 30) | def set_remote(self, col, row, val):
    method set_url (line 33) | def set_url(self, col, row, val):
    method iterload (line 36) | def iterload(self):
    method commitDeleteRow (line 41) | def commitDeleteRow(self, row):
    method commitAddRow (line 44) | def commitAddRow(self, row):
    method newRow (line 49) | def newRow(self):

FILE: visidata/apps/vgit/repos.py
  function guess_git (line 6) | def guess_git(vd, p):
  function open_git (line 12) | def open_git(vd, p):
  function git_repos (line 17) | def git_repos(vd, p, args):
  class GitLinesColumn (line 21) | class GitLinesColumn(Column):
    method __init__ (line 22) | def __init__(self, name, cmd, *args, **kwargs):
    method calcValue (line 29) | def calcValue(self, r):
  class GitAllColumn (line 35) | class GitAllColumn(GitLinesColumn):
    method calcValue (line 36) | def calcValue(self, r):
  class GitRepos (line 41) | class GitRepos(GitSheet):
    method iterload (line 60) | def iterload(self):
    method openRow (line 66) | def openRow(self, row):
    method openCell (line 69) | def openCell(self, col, row):

FILE: visidata/apps/vgit/stash.py
  function git_stash (line 9) | def git_stash(vd, p, args):
  class GitStashes (line 14) | class GitStashes(GitSheet):
    method iterload (line 32) | def iterload(self):
    method openRow (line 50) | def openRow(self, row):

FILE: visidata/apps/vgit/status.py
  function git_status (line 17) | def git_status(vd, p, args, **kwargs):
  class GitFile (line 23) | class GitFile:
    method __init__ (line 24) | def __init__(self, path, gitsrc):
    method __str__ (line 29) | def __str__(self):
  class GitStatus (line 32) | class GitStatus(GitSheet):
    method statusText (line 67) | def statusText(self, st):
    method workdir (line 82) | def workdir(self):
    method git_status (line 85) | def git_status(self, r):
    method ignored (line 101) | def ignored(self, fn):
    method remotediff (line 111) | def remotediff(self):
    method iterload (line 114) | def iterload(self):
    method openRow (line 156) | def openRow(self, row):
    method openRows (line 163) | def openRows(self, rows):
  function _cachedStatus (line 169) | def _cachedStatus(self):

FILE: visidata/apps/vgit/statusbar.py
  function progressStatus (line 7) | def progressStatus(sheet):
  function branchStatus (line 13) | def branchStatus(sheet):
  function gitInProgress (line 20) | def gitInProgress(sheet):

FILE: visidata/basesheet.py
  class LazyChainMap (line 9) | class LazyChainMap:
    method __init__ (line 11) | def __init__(self, *objs, locals=None):
    method __iter__ (line 19) | def __iter__(self):
    method __contains__ (line 22) | def __contains__(self, k):
    method keys (line 25) | def keys(self):
    method get (line 28) | def get(self, key, default=None):
    method clear (line 33) | def clear(self):
    method __getitem__ (line 36) | def __getitem__(self, k):
    method __setitem__ (line 42) | def __setitem__(self, k, v):
  class DrawablePane (line 49) | class DrawablePane(Extensible):
    method __init__ (line 50) | def __init__(self, **kwargs):
    method draw (line 54) | def draw(self, scr):
    method windowHeight (line 59) | def windowHeight(self):
    method windowWidth (line 64) | def windowWidth(self):
    method currow (line 69) | def currow(self):
    method execCommand2 (line 72) | def execCommand2(self, cmd, vdglobals=None):
  class _dualproperty (line 87) | class _dualproperty:
    method __init__ (line 89) | def __init__(self, obj_method, cls_method):
    method __get__ (line 93) | def __get__(self, obj, objtype=None):
  class BaseSheet (line 100) | class BaseSheet(DrawablePane):
    method _obj_options (line 110) | def _obj_options(self):
    method _class_options (line 113) | def _class_options(cls):
    method __init__ (line 118) | def __init__(self, *names, rows=UNLOADED, **kwargs):
    method setModified (line 132) | def setModified(self):
    method __lt__ (line 137) | def __lt__(self, other):
    method __copy__ (line 143) | def __copy__(self):
    method __bool__ (line 152) | def __bool__(self):
    method __len__ (line 156) | def __len__(self):
    method __str__ (line 160) | def __str__(self):
    method rows (line 164) | def rows(self):
    method rows (line 168) | def rows(self, rows):
    method nRows (line 172) | def nRows(self):
    method __contains__ (line 176) | def __contains__(self, vs):
    method displaySource (line 184) | def displaySource(self):
    method execCommand (line 195) | def execCommand(self, longname, vdglobals=None, keystrokes=None):
    method lastCommandThreads (line 237) | def lastCommandThreads(self):
    method names (line 241) | def names(self):
    method names (line 245) | def names(self, names):
    method name (line 252) | def name(self):
    method name (line 257) | def name(self, name):
    method maybeClean (line 264) | def maybeClean(self, s):
    method recalc (line 268) | def recalc(self):
    method refresh (line 272) | def refresh(self):
    method ensureLoaded (line 276) | def ensureLoaded(self):
    method reload (line 282) | def reload(self):
    method cursorRow (line 287) | def cursorRow(self):
    method checkCursor (line 291) | def checkCursor(self):
    method evalExpr (line 295) | def evalExpr(self, expr, **kwargs):
    method formatString (line 299) | def formatString(self, fmt, **kwargs):
  function redraw (line 305) | def redraw(vd):
  function sheet (line 317) | def sheet(self):
  function isLongname (line 322) | def isLongname(self, ks:str):
  function getSheet (line 328) | def getSheet(vd, sheetname):

FILE: visidata/bezier.py
  function bezier (line 4) | def bezier(x1, y1, x2, y2, x3, y3):
  function _recursive_bezier (line 11) | def _recursive_bezier(x1, y1, x2, y2, x3, y3, level=0):

FILE: visidata/canvas.py
  class Point (line 20) | class Point:
    method __init__ (line 21) | def __init__(self, x, y):
    method __repr__ (line 25) | def __repr__(self):
    method xy (line 32) | def xy(self):
  class Box (line 35) | class Box:
    method __init__ (line 36) | def __init__(self, x, y, w=0, h=0):
    method __repr__ (line 42) | def __repr__(self):
    method xymin (line 46) | def xymin(self):
    method xmax (line 50) | def xmax(self):
    method ymax (line 54) | def ymax(self):
    method center (line 58) | def center(self):
    method xcenter (line 62) | def xcenter(self):
    method ycenter (line 66) | def ycenter(self):
    method contains (line 69) | def contains(self, x, y):
  function BoundingBox (line 75) | def BoundingBox(x1, y1, x2, y2):
  function clipline (line 79) | def clipline(x1, y1, x2, y2, xmin, ymin, xmax, ymax):
  function iterline (line 111) | def iterline(x1, y1, x2, y2):
  function anySelected (line 132) | def anySelected(vs, rows):
  class Plotter (line 139) | class Plotter(BaseSheet):
    method __init__ (line 143) | def __init__(self, *names, **kwargs):
    method nRows (line 151) | def nRows(self):
    method resetCanvasDimensions (line 154) | def resetCanvasDimensions(self, windowHeight, windowWidth):
    method plotpixel (line 162) | def plotpixel(self, x, y, attr:"str|ColorAttr=''", row=None):
    method plotline (line 165) | def plotline(self, x1, y1, x2, y2, attr:"str|ColorAttr=''", row=None):
    method plotlabel (line 169) | def plotlabel(self, x, y, text, attr:"str|ColorAttr=''", row=None):
    method plotlegend (line 172) | def plotlegend(self, i, txt, attr:"str|ColorAttr=''", width=15):
    method plotterCursorBox (line 177) | def plotterCursorBox(self):
    method plotterMouse (line 182) | def plotterMouse(self):
    method plotterFromTerminalCoord (line 185) | def plotterFromTerminalCoord(self, x, y):
    method getPixelAttrRandom (line 188) | def getPixelAttrRandom(self, x, y) -> str:
    method getPixelAttrMost (line 194) | def getPixelAttrMost(self, x, y) -> str:
    method hideAttr (line 205) | def hideAttr(self, attr:str, hide=True):
    method rowsWithin (line 212) | def rowsWithin(self, plotter_bbox, invert_y=False):
    method draw (line 235) | def draw(self, scr):
    method draw_empty (line 243) | def draw_empty(self, scr):
    method draw_pixels (line 256) | def draw_pixels(self, scr, clear_empty_squares=True):
    method draw_labels (line 302) | def draw_labels(self, scr):
  class Canvas (line 352) | class Canvas(Plotter):
    method __init__ (line 361) | def __init__(self, *names, **kwargs):
    method nRows (line 382) | def nRows(self):
    method reset (line 385) | def reset(self):
    method plotColor (line 397) | def plotColor(self, k) -> str:
    method resetCanvasDimensions (line 413) | def resetCanvasDimensions(self, windowHeight, windowWidth):
    method statusLine (line 450) | def statusLine(self):
    method canvasMouse (line 454) | def canvasMouse(self):
    method setCursorSize (line 461) | def setCursorSize(self, p):
    method setCursorSizeInPlotterPixels (line 468) | def setCursorSizeInPlotterPixels(self, w, h):
    method formatX (line 472) | def formatX(self, v):
    method formatY (line 475) | def formatY(self, v):
    method commandCursor (line 478) | def commandCursor(sheet, execstr):
    method canvasCharWidth (line 493) | def canvasCharWidth(self):
    method canvasCharHeight (line 498) | def canvasCharHeight(self):
    method plotterVisibleBox (line 503) | def plotterVisibleBox(self):
    method plotterCursorBox (line 510) | def plotterCursorBox(self):
    method startCursor (line 518) | def startCursor(self):
    method point (line 526) | def point(self, x, y, attr:"str|ColorAttr=''", row=None):
    method line (line 529) | def line(self, x1, y1, x2, y2, attr:"str|ColorAttr=''", row=None):
    method polyline (line 532) | def polyline(self, vertexes, attr:"str|ColorAttr=''", row=None):
    method polygon (line 536) | def polygon(self, vertexes, attr:"str|ColorAttr=''", row=None):
    method qcurve (line 540) | def qcurve(self, vertexes, attr:"str|ColorAttr=''", row=None):
    method label (line 552) | def label(self, x, y, text, attr:"str|ColorAttr=''", row=None):
    method fixPoint (line 555) | def fixPoint(self, plotterPoint, canvasPoint):
    method zoomTo (line 561) | def zoomTo(self, bbox):
    method incrZoom (line 568) | def incrZoom(self, incr):
    method resetBounds (line 574) | def resetBounds(self, refresh=True):
    method calcTopCursorY (line 618) | def calcTopCursorY(self):
    method calcBottomCursorY (line 627) | def calcBottomCursorY(self):
    method plotlegends (line 631) | def plotlegends(self):
    method checkCursor (line 640) | def checkCursor(self):
    method xScaler (line 651) | def xScaler(self):
    method yScaler (line 660) | def yScaler(self):
    method calcVisibleBoxWidth (line 668) | def calcVisibleBoxWidth(self):
    method calcVisibleBoxHeight (line 681) | def calcVisibleBoxHeight(self):
    method scaleX (line 694) | def scaleX(self, canvasX) -> int:
    method scaleY (line 698) | def scaleY(self, canvasY) -> int:
    method unscaleX (line 702) | def unscaleX(self, plotterX):
    method unscaleY (line 706) | def unscaleY(self, plotterY):
    method canvasW (line 710) | def canvasW(self, plotter_width):
    method canvasH (line 714) | def canvasH(self, plotter_height):
    method refresh (line 718) | def refresh(self):
    method render (line 722) | def render(self, h, w):
    method render_async (line 732) | def render_async(self):
    method plot_elements (line 735) | def plot_elements(self, invert_y=False):
    method deleteSourceRows (line 783) | def deleteSourceRows(self, rows):

FILE: visidata/canvas_text.py
  function boundingBox (line 5) | def boundingBox(rows):
  class CharBox (line 18) | class CharBox:
    method __init__ (line 19) | def __init__(self, scr=None, x1=0, y1=0, w=None, h=None):
    method __str__ (line 29) | def __str__(self):
    method normalize (line 32) | def normalize(self):
    method x2 (line 43) | def x2(self):
    method x2 (line 47) | def x2(self, v):
    method y2 (line 52) | def y2(self):
    method y2 (line 56) | def y2(self, v):
    method contains (line 60) | def contains(self, b):
  class TextCanvas (line 69) | class TextCanvas(BaseSheet):
    method rows (line 71) | def rows(self):
    method rows (line 75) | def rows(self, v):
    method minXY (line 79) | def minXY(self) -> int:
    method maxXY (line 84) | def maxXY(self) -> int:
    method reload (line 88) | def reload(self):
    method draw (line 91) | def draw(self, scr):
    method commandCursor (line 96) | def commandCursor(self, execstr):
    method checkCursor (line 101) | def checkCursor(self):
    method iterbox (line 105) | def iterbox(self, box, n=None):
    method itercursor (line 115) | def itercursor(self, n=None):
    method cursorRows (line 119) | def cursorRows(self):
    method slide (line 122) | def slide(self, rows, dx, dy):

FILE: visidata/choose.py
  function chooseOne (line 8) | def chooseOne(vd, choices, type=''):
  function choose (line 14) | def choose(vd, choices, n=None, type=''):
  class ChoiceSheet (line 22) | class ChoiceSheet(ListOfDictSheet):
    method makeChoice (line 25) | def makeChoice(self, rows):
  function chooseFancy (line 30) | def chooseFancy(vd, choices):
  function chooseMany (line 42) | def chooseMany(vd, choices, type=''):

FILE: visidata/clean_names.py
  function cleanName (line 9) | def cleanName(vd, s):
  function maybeClean (line 19) | def maybeClean(sheet, s):

FILE: visidata/clipboard.py
  function setClipboardRows (line 40) | def setClipboardRows(vd, rows):
  function getClipboardRows (line 44) | def getClipboardRows(vd):
  function setClipboardCols (line 48) | def setClipboardCols(vd, cols):
  function getClipboardCols (line 52) | def getClipboardCols(vd):
  function copyRows (line 56) | def copyRows(sheet, rows):
  function copyCells (line 65) | def copyCells(sheet, col, rows):
  function syscopyValue (line 74) | def syscopyValue(sheet, val):
  function setColClipboard (line 86) | def setColClipboard(sheet):
  function syscopyCells (line 93) | def syscopyCells(sheet, cols, rows, filetype=None):
  function syscopyCells_async (line 100) | def syscopyCells_async(sheet, cols, rows, filetype):
  function sysclipValue (line 120) | def sysclipValue(vd):
  function pasteFromClipboard (line 127) | def pasteFromClipboard(vd, cols, rows):
  function delete_row (line 148) | def delete_row(sheet, rowidx):
  function paste_after (line 168) | def paste_after(sheet, rowidx):

FILE: visidata/cliptext.py
  function wcwidth (line 44) | def wcwidth(cc, ambig=1):
  function is_vdcode (line 60) | def is_vdcode(s:str) -> bool:
  function escape_vdcode (line 64) | def escape_vdcode(s:str) -> str:
  function is_marked_literal (line 68) | def is_marked_literal(s):
  function iterchunks (line 71) | def iterchunks(s, literal=False):
  function dispwidth (line 111) | def dispwidth(ss, maxwidth=None, literal=False):
  function _dispch (line 126) | def _dispch(c, oddspacech=None, combch=None, modch=None):
  function iterchars (line 139) | def iterchars(x):
  function _clipstr (line 160) | def _clipstr(s, dispw, trunch='', oddspacech='', combch='', modch=''):
  function clipstr (line 204) | def clipstr(s, dispw, truncator=None, oddspace=None):
  function clipdraw (line 221) | def clipdraw(scr, y, x, s, attr, w=None, clear=True, literal=False, **kw...
  function _addstr (line 238) | def _addstr(scr, y, x, s, attr):
  function clipdraw_chunks (line 246) | def clipdraw_chunks(scr, y, x, chunks, cattr:ColorAttr=ColorAttr(), w=No...
  function _markdown_to_internal (line 322) | def _markdown_to_internal(text):
  function wraptext (line 332) | def wraptext(text, width=80, indent=''):
  function clipstr_start (line 423) | def clipstr_start(dispval, w, truncator='', literal=False):
  function clipstr_middle (line 441) | def clipstr_middle(s, n=10, truncator='…'):
  function clip_markup_middle (line 457) | def clip_markup_middle(s:str, w:int):

FILE: visidata/cmdlog.py
  function queueCommand (line 27) | def queueCommand(vd, longname, input=None, sheet=None, col=None, row=None):
  function open_vd (line 33) | def open_vd(vd, p):
  function open_vdj (line 37) | def open_vdj(vd, p):
  function save_vdj (line 44) | def save_vdj(vd, p, *vsheets):
  function checkVersion (line 52) | def checkVersion(vd, desired_version):
  function fnSuffix (line 57) | def fnSuffix(vd, prefix:str):
  function indexMatch (line 66) | def indexMatch(L, func):
  function isLoggableCommand (line 73) | def isLoggableCommand(vd, cmd):
  function isLoggableSheet (line 83) | def isLoggableSheet(sheet):
  function moveToRow (line 88) | def moveToRow(vs, rowstr):
  function getRowIndexFromStr (line 99) | def getRowIndexFromStr(vs, row):
  function moveToCol (line 117) | def moveToCol(vs, col):
  function commandCursor (line 133) | def commandCursor(sheet, execstr):
  class CommandLogBase (line 149) | class CommandLogBase:
    method newRow (line 167) | def newRow(self, **fields):
    method beforeExecHook (line 170) | def beforeExecHook(self, sheet, cmd, args, keystrokes):
    method afterExecSheet (line 196) | def afterExecSheet(self, sheet, escaped, err):
    method openHook (line 213) | def openHook(self, vs, src):
  class CommandLog (line 220) | class CommandLog(CommandLogBase, VisiDataMetaSheet):
  class CommandLogJsonl (line 223) | class CommandLogJsonl(CommandLogBase, JsonLinesSheet):
    method newRow (line 227) | def newRow(self, **fields):
    method iterload (line 230) | def iterload(self):
  function replay_cancel (line 246) | def replay_cancel(vd):
  function moveToReplayContext (line 253) | def moveToReplayContext(vd, r, vs):
  function replayOne (line 267) | def replayOne(vd, r):
  class DisableAsync (line 320) | class DisableAsync:
    method __enter__ (line 321) | def __enter__(self):
    method __exit__ (line 324) | def __exit__(self, exc_type, exc_val, tb):
  function replay_sync (line 329) | def replay_sync(vd, cmdlog):
  function replay (line 370) | def replay(vd, cmdlog):
  function getLastArgs (line 377) | def getLastArgs(vd):
  function setLastArgs (line 385) | def setLastArgs(vd, args):
  function replayStatus (line 394) | def replayStatus(vd):
  function cmdlog (line 405) | def cmdlog(sheet):
  function cmdlog_sheet (line 413) | def cmdlog_sheet(sheet):
  function shortcut (line 425) | def shortcut(self):
  function cmdlog (line 442) | def cmdlog(vd):
  function modifyCommand (line 450) | def modifyCommand(vd):

FILE: visidata/color.py
  class ColorAttr (line 23) | class ColorAttr:
    method __init__ (line 30) | def __init__(self, fg:int=-1, bg:int=-1, attributes:int=0, precedence:...
    method update (line 39) | def update(self, b:'ColorAttr', prec:int=10) -> 'ColorAttr':
    method attr (line 43) | def attr(self) -> int:
    method as_str (line 48) | def as_str(self) -> str:
  function update_attr (line 54) | def update_attr(oldattr:ColorAttr, updattr:ColorAttr, updprec:int=None) ...
  class ColorMaker (line 78) | class ColorMaker:
    method __init__ (line 79) | def __init__(self):
    method colorattr_cache (line 84) | def colorattr_cache(self):
    method setup (line 88) | def setup(self):
    method colors (line 95) | def colors(self):
    method __getitem__ (line 99) | def __getitem__(self, colorname:str) -> ColorAttr:
    method __getattr__ (line 103) | def __getattr__(self, optname) -> ColorAttr:
    method resolve_colors (line 108) | def resolve_colors(self, colorstack):
    method get_color (line 116) | def get_color(self, optname:str, precedence:int=0) -> ColorAttr:
    method _split_colorstr (line 126) | def _split_colorstr(self, colorstr):
    method _get_colornum (line 153) | def _get_colornum(self, colorname:'str|int', default:int=-1) -> int:
    method _attrnames_to_num (line 182) | def _attrnames_to_num(self, attrnames:'list[str]') -> int:
    method _attrs (line 189) | def _attrs(self):
    method _colornames_to_cattr (line 193) | def _colornames_to_cattr(self, colorname:str, precedence=0) -> ColorAttr:
    method _get_colorpair (line 202) | def _get_colorpair(self, fg:'int|None', bg:'int|None', colorname:str) ...
  function rgb_to_xterm256 (line 224) | def rgb_to_xterm256(r:int,g:int,b:int,a:int=255) -> int:
  function xterm256_to_css (line 242) | def xterm256_to_css(n:'str|int') -> str:
  function xterm256_to_rgb (line 248) | def xterm256_to_rgb(n:'str|int') -> tuple:
  function rgb_to_attr (line 285) | def rgb_to_attr(r:int,g:int,b:int,a:int=255) -> str:
  function css_to_xterm256 (line 289) | def css_to_xterm256(csscolor:str) -> str:

FILE: visidata/column.py
  class InProgress (line 16) | class InProgress(Exception):
    method stacktrace (line 18) | def stacktrace(self):
  class DisplayWrapper (line 28) | class DisplayWrapper:
    method __init__ (line 29) | def __init__(self, value=None, *, typedval=None, text='', note=None, n...
    method __bool__ (line 37) | def __bool__(self):
    method __eq__ (line 40) | def __eq__(self, other):
  class Column (line 44) | class Column(Extensible):
    method __init__ (line 61) | def __init__(self, name=None, *, type=anytype, cache=False, **kwargs):
    method __copy__ (line 88) | def __copy__(self):
    method __str__ (line 97) | def __str__(self):
    method __repr__ (line 100) | def __repr__(self):
    method __deepcopy__ (line 103) | def __deepcopy__(self, memo):
    method __getstate__ (line 106) | def __getstate__(self):
    method __setstate__ (line 109) | def __setstate__(self, d):
    method recalc (line 113) | def recalc(self, sheet=None):
    method name (line 122) | def name(self):
    method name (line 127) | def name(self, name):
    method setName (line 130) | def setName(self, name):
    method typestr (line 144) | def typestr(self):
    method typestr (line 149) | def typestr(self, v):
    method type (line 153) | def type(self):
    method type (line 158) | def type(self, t):
    method width (line 169) | def width(self):
    method width (line 174) | def width(self, w):
    method formatted_help (line 181) | def formatted_help(self):
    method help_formatters (line 185) | def help_formatters(self):
    method help_displayers (line 190) | def help_displayers(self):
    method _formatdict (line 195) | def _formatdict(col):
    method fmtstr (line 201) | def fmtstr(self):
    method fmtstr (line 206) | def fmtstr(self, v):
    method _format_len (line 209) | def _format_len(self, typedval, **kwargs):
    method formatter_len (line 217) | def formatter_len(self, fmtstr):
    method formatter_generic (line 220) | def formatter_generic(self, fmtstr):
    method formatter_json (line 223) | def formatter_json(self, fmtstr):
    method formatter_python (line 226) | def formatter_python(self, fmtstr):
    method make_formatter (line 230) | def make_formatter(self):
    method format (line 235) | def format(self, *args, **kwargs):
    method formatValue (line 238) | def formatValue(self, typedval, width=None):
    method displayer_generic (line 258) | def displayer_generic(self, dw:DisplayWrapper, width=None):
    method displayer_full (line 271) | def displayer_full(self, dw:DisplayWrapper, width=None):
    method display (line 282) | def display(self, *args, **kwargs):
    method hide (line 286) | def hide(self, hide=True):
    method readonly (line 293) | def readonly(self):
    method hidden (line 297) | def hidden(self):
    method calcValue (line 303) | def calcValue(self, row):
    method getTypedValue (line 307) | def getTypedValue(self, row):
    method setCache (line 311) | def setCache(self, cache):
    method _calcIntoCacheAsync (line 321) | def _calcIntoCacheAsync(self, row):
    method _calcIntoCache (line 326) | def _calcIntoCache(self, row):
    method getValue (line 332) | def getValue(self, row):
    method getCell (line 360) | def getCell(self, row):
    method getDisplayValue (line 423) | def getDisplayValue(self, row):
    method getFullDisplayValue (line 428) | def getFullDisplayValue(self, row):
    method putValue (line 438) | def putValue(self, row, val):
    method setValue (line 443) | def setValue(self, row, val, setModified=True):
    method setValues (line 453) | def setValues(self, rows, *values):
    method setValuesTyped (line 473) | def setValuesTyped(self, rows, *values):
    method getMaxWidth (line 492) | def getMaxWidth(self, rows):
    method measureValueWidthCapped (line 505) | def measureValueWidthCapped(self, row, maxwidth=None):
  class WritableColumn (line 527) | class WritableColumn(Column):
    method readonly (line 529) | def readonly(self):
  class AttrColumn (line 532) | class AttrColumn(WritableColumn):
    method __init__ (line 534) | def __init__(self, name=None, expr=None, **kwargs):
    method putValue (line 540) | def putValue(self, row, val):
  class ItemColumn (line 545) | class ItemColumn(WritableColumn):
    method __init__ (line 547) | def __init__(self, name=None, expr=None, **kwargs):
    method putValue (line 553) | def putValue(self, row, val):
  class SubColumnFunc (line 558) | class SubColumnFunc(Column):
    method __init__ (line 560) | def __init__(self, name='', origcol=None, expr=None, subfunc=getitemde...
    method readonly (line 566) | def readonly(self):
    method calcValue (line 569) | def calcValue(self, row):
    method putValue (line 575) | def putValue(self, row, value):
    method recalc (line 581) | def recalc(self, sheet=None):
  function SubColumnAttr (line 586) | def SubColumnAttr(attrname, c, **kwargs):
  function SubColumnItem (line 591) | def SubColumnItem(idx, c, **kwargs):
  class SettableColumn (line 597) | class SettableColumn(WritableColumn):
    method putValue (line 599) | def putValue(self, row, value):
    method calcValue (line 602) | def calcValue(self, row):

FILE: visidata/ddwplay.py
  class AttrDict (line 9) | class AttrDict(dict):
    method __getattr__ (line 10) | def __getattr__(self, k):
    method __setattr__ (line 20) | def __setattr__(self, k, v):
    method __dir__ (line 22) | def __dir__(self):
  class Animation (line 26) | class Animation:
    method __init__ (line 27) | def __init__(self, fp):
    method iterdeep (line 34) | def iterdeep(self, rows, x=0, y=0, parents=None, **kwargs):
    method matches (line 49) | def matches(self, row, values):
    method load_from (line 59) | def load_from(self, fp):
    method draw (line 83) | def draw(self, scr, *, t=0, x=0, y=0, loop=False, attr=ColorAttr(), **...
  class AnimationMgr (line 108) | class AnimationMgr:
    method __init__ (line 109) | def __init__(self):
    method trigger (line 113) | def trigger(self, name, **kwargs):
    method load (line 119) | def load(self, name, fp):
    method maxHeight (line 123) | def maxHeight(self):
    method maxWidth (line 127) | def maxWidth(self):
    method draw (line 130) | def draw(self, scr, t=None, **kwargs):

FILE: visidata/deprecated.py
  function deprecated_alias (line 6) | def deprecated_alias(depver, *args, **kwargs):
  function deprecated_warn (line 12) | def deprecated_warn(vd, funcname, ver, instead):
  function deprecated (line 27) | def deprecated(ver, instead='', check=True):
  function _deprecated_api (line 41) | def _deprecated_api(ver, instead=''):
  function __call__ (line 48) | def __call__(vd):
  function copyToClipboard (line 54) | def copyToClipboard(value):
  function replayableOption (line 60) | def replayableOption(optname, default, helpstr):
  function SubrowColumn (line 64) | def SubrowColumn(*args, **kwargs):
  function DeferredSetColumn (line 68) | def DeferredSetColumn(*args, **kwargs):
  function bindkey_override (line 72) | def bindkey_override(keystrokes, longname):
  function exec_keystrokes (line 80) | def exec_keystrokes(self, keystrokes, vdglobals=None):
  function filetype (line 87) | def filetype(vd, ext, constructor):
  function joinSheetnames (line 93) | def joinSheetnames(vd, *sheetnames):
  function load_pyobj (line 99) | def load_pyobj(*names, **kwargs):
  function push_pyobj (line 104) | def push_pyobj(name, pyobj):
  function isNumeric (line 112) | def isNumeric(col):
  function clean_name (line 151) | def clean_name(s):
  function maybe_clean (line 154) | def maybe_clean(s, vs):
  function load_tsv (line 159) | def load_tsv(fn):
  function itemsetter (line 185) | def itemsetter(i):
  function clean_to_id (line 200) | def clean_to_id(s):
  class OnExit (line 204) | class OnExit:
    method __init__ (line 206) | def __init__(self, func, *args, **kwargs):
    method __enter__ (line 211) | def __enter__(self):
    method __exit__ (line 214) | def __exit__(self, exc_type, exc_value, exc_traceback):
  function inputRegexSubstOld (line 225) | def inputRegexSubstOld(vd, prompt):
  function setValueSafe (line 244) | def setValueSafe(self, row, value):
  function checkCursorNoExceptions (line 250) | def checkCursorNoExceptions(sheet):
  function memo (line 255) | def memo(vd, name, col, row):
  function keystr (line 264) | def keystr(sheet, row):
  function toggleKeys (line 271) | def toggleKeys(self, cols):

FILE: visidata/editor.py
  class SuspendCurses (line 12) | class SuspendCurses:
    method __enter__ (line 14) | def __enter__(self):
    method __exit__ (line 20) | def __exit__(self, exc_type, exc_val, tb):
  function launchEditor (line 28) | def launchEditor(vd, *args):
  function launchBrowser (line 37) | def launchBrowser(vd, *args):
  function launchExternalEditor (line 46) | def launchExternalEditor(vd, v, linenum=0):
  function launchExternalEditorPath (line 57) | def launchExternalEditorPath(vd, path, linenum=0):
  function suspend (line 73) | def suspend(vd):
  function _breakpoint (line 79) | def _breakpoint(*args, **kwargs):

FILE: visidata/errors.py
  class ExpectedException (line 10) | class ExpectedException(Exception):
  function stacktrace (line 15) | def stacktrace(e=None, exclude_caller=False):
  function exceptionCaught (line 55) | def exceptionCaught(vd, exc=None, status=True, **kwargs):

FILE: visidata/experimental/daw/merge_transcripts.py
  function stderr (line 8) | def stderr(*args):
  function progress (line 11) | def progress(s):
  function find_words_around (line 15) | def find_words_around(needle:dict, haystack:list[dict], seconds=10):
  function _score (line 20) | def _score(whisperw, humanw):
  function clean (line 32) | def clean(s:str) -> str:
  function main (line 35) | def main(humanfn, *whisperfns):

FILE: visidata/experimental/daw/mpv.py
  class MpvProcess (line 15) | class MpvProcess:
    method __init__ (line 71) | def __init__(self, sourcefn, source:Sheet):
    method add_filter (line 76) | def add_filter(self, filtername):
    method remove_filter (line 82) | def remove_filter(self, filtername):
    method set_filter_parm (line 85) | def set_filter_parm(self, filtername, parmname, val):
    method mpvsockfn (line 92) | def mpvsockfn(self):
    method restart_mpv (line 95) | def restart_mpv(self, t:float):
    method is_default (line 101) | def is_default(self, filtername, parmname, val):
    method start_mpv (line 104) | def start_mpv(self):
    method mpv_command (line 131) | def mpv_command(self, **kwargs):
    method mpv_query (line 137) | def mpv_query(self, propname):
    method set_property (line 154) | def set_property(self, propname, b=True):
    method paused (line 158) | def paused(self):
    method playback_time (line 164) | def playback_time(self):
    method pause_audio (line 169) | def pause_audio(self, b=True):
    method play_audio (line 172) | def play_audio(self, t:float):
    method seek_audio (line 176) | def seek_audio(self, dt:float, *args):

FILE: visidata/experimental/daw/vdaw.py
  function to_hms (line 23) | def to_hms(t:float, width=None) -> str:
  function cleanword (line 48) | def cleanword(s:str) -> str:
  function is_cut (line 51) | def is_cut(row):
  function replace_subrows (line 58) | def replace_subrows(row):
  function open_transcript (line 74) | def open_transcript(vd, p):
  function formatter_hhmmss (line 80) | def formatter_hhmmss(self, fmtstr):
  class EditRow (line 84) | class EditRow:
    method __init__ (line 85) | def __init__(self, section:str='', speaker:str='', header:str='', subr...
    method __str__ (line 97) | def __str__(self):
    method to_json (line 101) | def to_json(self) -> dict:
    method __contains__ (line 110) | def __contains__(self, t:float) -> bool:
    method start (line 118) | def start(self) -> float:
    method end (line 123) | def end(self) -> float:
    method duration (line 128) | def duration(self) -> float:
    method raw_duration (line 137) | def raw_duration(self) -> float:
    method text (line 141) | def text(self) -> str:
    method editedtext (line 151) | def editedtext(self) -> str:
    method cookedtext (line 164) | def cookedtext(self) -> str:
    method uncutrows (line 174) | def uncutrows(self) -> list:
    method nwords (line 180) | def nwords(self) -> int:
    method split_at_word (line 183) | def split_at_word(self, n:int) -> tuple['EditRow', 'EditRow']:
    method split_at_time (line 214) | def split_at_time(self, t:float) -> tuple['EditRow', 'EditRow']:
  class PodcastEditingSheet (line 258) | class PodcastEditingSheet(Sheet):
    method iterload (line 288) | def iterload(self):
    method openRows (line 329) | def openRows(self, rows):
    method openRow (line 340) | def openRow(self, row):
    method combine_rows (line 346) | def combine_rows(self, rows):
    method expand_row (line 359) | def expand_row(self, rowidx):
    method bump (line 371) | def bump(self, n, *rows):
    method set_speaker_recursive (line 378) | def set_speaker_recursive(self, row, speaker):
    method set_text_recursive (line 385) | def set_text_recursive(self, row, newtext):
    method cycle_speaker (line 409) | def cycle_speaker(self, row):
    method getRowIndexByPlaytime (line 416) | def getRowIndexByPlaytime(self, t:float, rows=None) -> int:
    method checkCursor (line 422) | def checkCursor(self):
    method find_row_path (line 449) | def find_row_path(self, t:float, rows:list['EditRow']=None) -> list['E...
    method is_cut (line 474) | def is_cut(self, t:float):
    method go_playhead (line 480) | def go_playhead(self):
    method split_at_playhead (line 484) | def split_at_playhead(self):
    method split_at_input (line 495) | def split_at_input(self, rowidx, word:str):
    method go_header_next (line 513) | def go_header_next(self, didx:int, startrow:int):
    method playheadStatus (line 523) | def playheadStatus(self):
    method setFilterParmByIndex (line 530) | def setFilterParmByIndex(self, filtername, parmname, idxvalue):
    method reformat_row (line 533) | def reformat_row(self, rowidx, undo=True):
    method reformat_rows (line 554) | def reformat_rows(self, rows):
    method bulk_combine (line 560) | def bulk_combine(self, rows):
    method speed_change (line 564) | def speed_change(self, dv):
    method words (line 569) | def words(self):
    method flag_bad_timings (line 574) | def flag_bad_timings(self):
  function iterwords (line 619) | def iterwords(segs):
  class FilterParametersSheet (line 628) | class FilterParametersSheet(Sheet):
    method iterload (line 639) | def iterload(self):
  function save_cutlist (line 651) | def save_cutlist(vd, p, sheet):
  function iterspeakerrows (line 676) | def iterspeakerrows(rows, include_cuts=True, inline_interjections=False,...
  function save_xmd (line 732) | def save_xmd(vd, p, sheet):
  function _default (line 757) | def _default(self, obj):
  function save_transcript (line 764) | def save_transcript(vd, p, sheet):

FILE: visidata/experimental/daw/xmd2json.py
  function parse_hhmmss (line 15) | def parse_hhmmss(hms:str) -> float:
  function interpolate_times (line 22) | def interpolate_times(pending_rows:list[dict], startt, endt):
  function split_cuts (line 34) | def split_cuts(row, startcut=False):
  function parse_xmd (line 48) | def parse_xmd(xmdfn:str) -> list:
  function main (line 104) | def main(xmdfn):

FILE: visidata/experimental/diff_sheet.py
  function makeDiffColorizer (line 9) | def makeDiffColorizer(othersheet):
  function setDiffSheet (line 25) | def setDiffSheet(vs):

FILE: visidata/experimental/gdrive.py
  function open_gdrive (line 7) | def open_gdrive(vd, p):
  function _drivebuild (line 22) | def _drivebuild(vd):
  function _gdrive (line 26) | def _gdrive(self):
  function _gdrive_rw (line 30) | def _gdrive_rw(self):
  class GDriveSheet (line 34) | class GDriveSheet(Sheet):
    method iterload (line 48) | def iterload(self):
    method openRow (line 67) | def openRow(self, r):
    method deleteFile (line 75) | def deleteFile(self, **kwargs):
    method putChanges (line 81) | def putChanges(self):

FILE: visidata/experimental/gsheets.py
  function open_gsheets (line 8) | def open_gsheets(vd, p):
  function google_discovery (line 16) | def google_discovery(self):
  function _gsheets (line 23) | def _gsheets(vd):
  function _gsheets_rw (line 28) | def _gsheets_rw(vd):
  class GSheetsIndex (line 32) | class GSheetsIndex(Sheet):
    method iterload (line 39) | def iterload(self):
    method openRow (line 46) | def openRow(self, r):
  class GSheet (line 50) | class GSheet(SequenceSheet):
    method iterload (line 52) | def iterload(self):
  function save_gsheets (line 58) | def save_gsheets(vd, p, *sheets):

FILE: visidata/experimental/live_search.py
  function dup_search (line 6) | def dup_search(sheet, cols='cursorCol'):

FILE: visidata/experimental/liveupdate.py
  function updateExpr (line 5) | def updateExpr(col, val):
  function expr (line 16) | def expr(self, expr):
  function addcol_expr (line 25) | def addcol_expr(sheet):

FILE: visidata/experimental/llm.py
  function anthropic_client (line 67) | def anthropic_client(vd):
  function loadLLMTemplate (line 75) | def loadLLMTemplate(vd):
  function queryLLM (line 90) | def queryLLM(vd, prompt):
  function llmAnalyzeColumn (line 123) | def llmAnalyzeColumn(sheet, col, rows, query):

FILE: visidata/experimental/mark.py
  function marks (line 10) | def marks(vd):
  class MarkSheet (line 14) | class MarkSheet(TableSheet):
  class MarksSheet (line 18) | class MarksSheet(TableSheet):
    method __init__ (line 31) | def __init__(self, *args, **kwargs):
    method getColor (line 38) | def getColor(self, sheet, row):
    method getMark (line 44) | def getMark(self, sheet, row):
    method getMarks (line 51) | def getMarks(self, row):
    method isMarked (line 55) | def isMarked(self, row, mark):
    method getMarkRow (line 59) | def getMarkRow(self, sheet, mark):
    method setMark (line 67) | def setMark(self, sheet, row, mark):
    method unsetMark (line 76) | def unsetMark(self, sheet, row, mark):
    method inputmark (line 82) | def inputmark(self):
    method openRow (line 85) | def openRow(self, row):
  function mark (line 91) | def mark(vd, sheet, rows, m):
  function unmark (line 97) | def unmark(vd, sheet, rows, m):

FILE: visidata/experimental/noahs_tapestry/tapestry.py
  function getNoahsPath (line 13) | def getNoahsPath(vd, name):
  function openNoahsText (line 17) | def openNoahsText(vd, name):
  function noahsDatabase (line 21) | def noahsDatabase(vd):
  class NoahsPuzzle (line 24) | class NoahsPuzzle(Sheet):
    method iterload (line 40) | def iterload(self):
  function noahsSolutions (line 54) | def noahsSolutions(vd):
  function solve_puzzle (line 58) | def solve_puzzle(vd, answer):
  class Tapestry (line 68) | class Tapestry(Canvas):
    method guide (line 70) | def guide(self):
    method reload (line 88) | def reload(self):
    method keep_running (line 97) | def keep_running(self):
    method draw (line 101) | def draw(self, scr):
    method open_puzzle (line 120) | def open_puzzle(self, puznum=None):
  function noahsTapestry (line 133) | def noahsTapestry(vd):

FILE: visidata/experimental/rownum.py
  function calcRowIndex (line 7) | def calcRowIndex(sheet, indexes):
  function _rowindex (line 13) | def _rowindex(sheet):
  function rowindex (line 20) | def rowindex(sheet, row):
  function prev (line 26) | def prev(sheet, row):
  function addcol_rowindex (line 33) | def addcol_rowindex(sheet, newcol):
  function addcol_delta (line 51) | def addcol_delta(sheet, vcolidx):
  function addcol_rownum (line 63) | def addcol_rownum(sheet):

FILE: visidata/experimental/slide_cells.py
  function slide_cells_right (line 13) | def slide_cells_right(sheet, row, vcolidx):

FILE: visidata/experimental/sort_selected.py
  function sortSelected (line 6) | def sortSelected(self, ordering):

FILE: visidata/expr.py
  class ExprColumn (line 6) | class ExprColumn(Column):
    method __init__ (line 8) | def __init__(self, name, expr=None, **kwargs):
    method calcValue (line 15) | def calcValue(self, row):
    method expr (line 25) | def expr(self):
    method expr (line 29) | def expr(self, expr):
  class CompleteExpr (line 34) | class CompleteExpr:
    method __init__ (line 35) | def __init__(self, sheet=None):
    method __call__ (line 43) | def __call__(self, val, state):
  function setValuesFromExpr (line 69) | def setValuesFromExpr(self, rows, expr, **kwargs):
  function inputExpr (line 89) | def inputExpr(self, prompt, *args, **kwargs):
  function addcol_expr (line 94) | def addcol_expr(sheet, expr_input, **kwargs):  # #3022

FILE: visidata/extensible.py
  class Extensible (line 6) | class Extensible:
    method init (line 10) | def init(cls, membername, initfunc=lambda: None, copy=False):
    method superclasses (line 38) | def superclasses(cls):
    method api (line 46) | def api(cls, func):
    method before (line 57) | def before(cls, beforefunc):
    method after (line 72) | def after(cls, afterfunc):
    method class_api (line 88) | def class_api(cls, func):
    method property (line 106) | def property(cls, func):
    method lazy_property (line 116) | def lazy_property(cls, func):
    method cached_property (line 131) | def cached_property(cls, func):
    method clear_all_caches (line 143) | def clear_all_caches(cls):
  function cache (line 148) | def cache(func):
  function drawcache_property (line 161) | def drawcache_property(func):

FILE: visidata/features/addcol_audiometadata.py
  function get_mutagen_info (line 9) | def get_mutagen_info(path):
  class MutagenColumn (line 15) | class MutagenColumn(AttrColumn):
    method calcValue (line 16) | def calcValue(self, r):
  function audiometadata_columns (line 22) | def audiometadata_columns(sheet):

FILE: visidata/features/addcol_histogram.py
  class HistogramColumn (line 4) | class HistogramColumn(Column):
    method calcValue (line 5) | def calcValue(col, row):
  function addcol_histogram (line 12) | def addcol_histogram(sheet, col):  #2052
  function calc_histogram_bounds (line 22) | def calc_histogram_bounds(sheet, col):

FILE: visidata/features/canvas_save_svg.py
  function plot_sheet (line 16) | def plot_sheet(self, ax):
  function save_svg (line 57) | def save_svg(vd, p, *sheets):

FILE: visidata/features/change_precision.py
  function setcol_precision (line 20) | def setcol_precision(col, amount:int):

FILE: visidata/features/cmdpalette.py
  function add_to_input (line 23) | def add_to_input(v, i, value=''):
  function accept_input (line 33) | def accept_input(v, i, value=None):
  function accept_input_if_subset (line 36) | def accept_input_if_subset(v, i, value=''):
  function usedInputs (line 49) | def usedInputs(vd):
  function execCommand2 (line 53) | def execCommand2(sheet, cmd, *args, **kwargs):
  function inputPalette (line 57) | def inputPalette(sheet, prompt, items,
  function cmdlist (line 210) | def cmdlist(sheet):
  function inputLongname (line 220) | def inputLongname(sheet):
  function inputLongnameSimple (line 251) | def inputLongnameSimple(sheet):
  function exec_longname (line 258) | def exec_longname(sheet, longname):

FILE: visidata/features/colorbrewer.py
  class PalettesSheet (line 297) | class PalettesSheet(Sheet):
    method iterload (line 309) | def iterload(self):
  function cycle_palette (line 319) | def cycle_palette(obj):
  function set_palette (line 328) | def set_palette(obj, palname, n):

FILE: visidata/features/colorsheet.py
  class ColorSheet (line 5) | class ColorSheet(Sheet):
    method iterload (line 17) | def iterload(self):
    method draw (line 27) | def draw(self, scr):
  function colorsSheet (line 46) | def colorsSheet(vd):

FILE: visidata/features/command_server.py
  class SocketIO (line 13) | class SocketIO(io.RawIOBase):
    method __init__ (line 14) | def __init__(self, sock):
    method read (line 17) | def read(self, sz=-1):
    method seekable (line 21) | def seekable(self):
  function mainloop (line 26) | def mainloop(vd, scr):
  function command_listener (line 34) | def command_listener(vd, addr, port):
  function command_server (line 49) | def command_server(vd, conn):
  function capture_draw_object (line 67) | def capture_draw_object(sheet, topRowIndex=0, nScreenRows=25):

FILE: visidata/features/currency_to_usd.py
  function currency_rates_json (line 23) | def currency_rates_json(date='latest', base='USD'):
  function currency_rates (line 44) | def currency_rates():
  function currency_multiplier (line 48) | def currency_multiplier(src_currency, dest_currency):
  function USD (line 60) | def USD(s):

FILE: visidata/features/customdate.py
  function customdate (line 7) | def customdate(sheet, fmtstr):

FILE: visidata/features/dedupe.py
  function gen_identify_duplicates (line 30) | def gen_identify_duplicates(sheet):
  function select_duplicate_rows (line 59) | def select_duplicate_rows(sheet, duplicates=True):
  function dedupe_rows (line 84) | def dedupe_rows(sheet, suffix='_deduped'):

FILE: visidata/features/describe.py
  function isError (line 12) | def isError(col, row):
  class DescribeColumn (line 23) | class DescribeColumn(Column):
    method __init__ (line 24) | def __init__(self, name, **kwargs):
  class DescribeSheet (line 30) | class DescribeSheet(ColumnsSheet):
    method loader (line 57) | def loader(self):
    method reloadColumn (line 70) | def reloadColumn(self, srccol):
    method calcStatistic (line 99) | def calcStatistic(self, d, func, *args, **kwargs):
    method openCell (line 104) | def openCell(self, col, row):

FILE: visidata/features/expand_cols.py
  function getSampleRows (line 10) | def getSampleRows(sheet):
  function expandCols (line 25) | def expandCols(sheet, cols, rows=None, depth=0):
  function _createExpandedColumns (line 38) | def _createExpandedColumns(sampleValue, col, rows):
  function _ (line 44) | def _(sampleValue, col, vals):
  function _createExpandedColumnsNamedTuple (line 64) | def _createExpandedColumnsNamedTuple(col, val):
  function _ (line 72) | def _(sampleValue, col, vals):
  function expand (line 95) | def expand(col, rows):
  class ExpandedColumn (line 120) | class ExpandedColumn(WritableColumn):
    method calcValue (line 121) | def calcValue(self, row):
    method setValue (line 124) | def setValue(self, row, value, setModified=True):
  function contract_cols (line 132) | def contract_cols(sheet, cols, depth=1):  # depth == 0 means contract al...
  function expand_cols_deep (line 150) | def expand_cols_deep(sheet, cols, rows=None, depth=0):  # depth == 0 mea...
  function contract_source_cols (line 155) | def contract_source_cols(sheet, cols):
  class ColumnGroup (line 166) | class ColumnGroup(Column):
    method calcValue (line 167) | def calcValue(self, row):
    method expand (line 170) | def expand(self, rows):

FILE: visidata/features/fill.py
  function fillNullValues (line 6) | def fillNullValues(vd, col, rows):

FILE: visidata/features/freeze.py
  function resetCache (line 7) | def resetCache(col):
  function freeze_col (line 13) | def freeze_col(sheet, col):
  class StaticSheet (line 34) | class StaticSheet(Sheet):
    method __init__ (line 36) | def __init__(self, source):
    method resetCols (line 39) | def resetCols(self):
    method iterload (line 49) | def iterload(self):
  function setcol_freeze (line 64) | def setcol_freeze(sheet, unfrozen):  #2660  Contributed by @midichef

FILE: visidata/features/go_col.py
  function nextColRegex (line 7) | def nextColRegex(sheet, colregex):
  function nextColName (line 18) | def nextColName(sheet, show_cells=True):

FILE: visidata/features/graph_seaborn.py
  function plot_seaborn (line 10) | def plot_seaborn(vd, rows, xcols, ycols):
  function ext_plot_seaborn (line 17) | def ext_plot_seaborn(vd, rows, xcols, ycols):

FILE: visidata/features/graph_zoom_y.py
  function zoom_all_y (line 6) | def zoom_all_y(sheet):

FILE: visidata/features/hint_types.py
  function hint_type_int (line 4) | def hint_type_int(sheet):
  function hint_type_float (line 12) | def hint_type_float(sheet):

FILE: visidata/features/hlsearch.py
  function highlight_chunks (line 16) | def highlight_chunks(sheet, chunks, hp, hoffset, colwidth, notewidth, ca...
  function setHighlightRegex (line 69) | def setHighlightRegex(sheet, r, cols=[]):
  function highlight_input (line 84) | def highlight_input(sheet, cols=[]):
  function clear_search (line 92) | def clear_search(sheet):

FILE: visidata/features/incr.py
  function numrange (line 8) | def numrange(vd, n, step=1):
  function test_numrange (line 14) | def test_numrange(vd=None):
  function num (line 19) | def num(vd, *args):

FILE: visidata/features/join.py
  function ensureLoaded (line 12) | def ensureLoaded(vd, sheets):
  function _appendRowsAfterLoading (line 21) | def _appendRowsAfterLoading(joinsheet, origsheets):
  function join_sheets_cols (line 49) | def join_sheets_cols(vd, cols, jointype:str=''):
  function openJoin (line 63) | def openJoin(sheet, others, jointype=''):
  function joinkey (line 115) | def joinkey(sheetKeyCols, row):
  function groupRowsByKey (line 119) | def groupRowsByKey(sheets:dict, rowsBySheetKey, rowsByKey):
  class JoinKeyColumn (line 144) | class JoinKeyColumn(WritableColumn):
    method __init__ (line 145) | def __init__(self, name='', keycols=None, **kwargs):
    method calcValue (line 149) | def calcValue(self, row):
    method putValue (line 159) | def putValue(self, row, value):
    method recalc (line 164) | def recalc(self, sheet=None):
  class MergeColumn (line 170) | class MergeColumn(WritableColumn):
    method calcValue (line 172) | def calcValue(self, row):
    method putValue (line 180) | def putValue(self, row, value):
    method isDiff (line 185) | def isDiff(self, row, value):
  class JoinSheet (line 197) | class JoinSheet(Sheet):
    method loader (line 205) | def loader(self):
  class ExtendedColumn (line 271) | class ExtendedColumn(WritableColumn):
    method calcValue (line 272) | def calcValue(self, row):
    method putValue (line 278) | def putValue(self, row, value):
  function ExtendedSheet_reload (line 288) | def ExtendedSheet_reload(self, sheets):
  class ConcatColumn (line 330) | class ConcatColumn(WritableColumn):
    method getColBySheet (line 332) | def getColBySheet(self, s):
    method calcValue (line 335) | def calcValue(self, row):
    method setValue (line 341) | def setValue(self, row, v):
  class ConcatSheet (line 351) | class ConcatSheet(Sheet):
    method iterload (line 354) | def iterload(self):
  function inputJointype (line 379) | def inputJointype(vd):

FILE: visidata/features/known_cols.py
  function afterLoad (line 16) | def afterLoad(sheet):

FILE: visidata/features/layout.py
  function setWidth (line 4) | def setWidth(self, w):
  function toggleWidth (line 12) | def toggleWidth(self, width):
  function toggleMultiline (line 21) | def toggleMultiline(self):
  function unhide_cols (line 28) | def unhide_cols(vd, cols, rows):
  function hide_col (line 34) | def hide_col(vd, col):
  function hide_uniform_cols (line 40) | def hide_uniform_cols(sheet):

FILE: visidata/features/melt.py
  class MeltedSheet (line 14) | class MeltedSheet(Sheet):
    method getValueCols (line 19) | def getValueCols(self) -> dict:
    method resetCols (line 45) | def resetCols(self):
    method iterload (line 72) | def iterload(self):
  function openMelt (line 95) | def openMelt(sheet, regex='(.*)'):

FILE: visidata/features/normcol.py
  function normalize_name (line 52) | def normalize_name(name):
  function gen_normalize_names (line 70) | def gen_normalize_names(names):
  function normalize_column_names (line 91) | def normalize_column_names(sheet):

FILE: visidata/features/open_syspaste.py
  function open_syspaste (line 9) | def open_syspaste(sheet, filetype='tsv'):

FILE: visidata/features/ping.py
  function new_ping (line 11) | def new_ping(vd, p):
  function makePingStats (line 18) | def makePingStats(ip):
  class PingStatsSheet (line 22) | class PingStatsSheet(Sheet):
    method loader (line 37) | def loader(self):
  function PingColumn (line 44) | def PingColumn(name, ip):
  class PingSheet (line 48) | class PingSheet(Sheet):
    method __init__ (line 50) | def __init__(self, *names, source=None, **kwargs):
    method ping_response (line 54) | def ping_response(self, row, ip, data):
    method traceroute_response (line 61) | def traceroute_response(self, ip, data):
    method ping_error (line 76) | def ping_error(self, ip, data):
    method update_traces (line 81) | def update_traces(self, row, ip):
    method send_trace (line 96) | def send_trace(self, ip, n):
    method iterload (line 111) | def iterload(self):

FILE: visidata/features/procmgr.py
  function new_top (line 5) | def new_top(vd, p):
  class CPUStatsSheet (line 9) | class CPUStatsSheet(Sheet):
    method iterload (line 15) | def iterload(self):
  class MemStatsSheet (line 32) | class MemStatsSheet(Sheet):
    method iterload (line 42) | def iterload(self):
  class UsefulProcessesSheet (line 64) | class UsefulProcessesSheet(Sheet):
    method iterload (line 77) | def iterload(self):
  class ProcessesSheet (line 83) | class ProcessesSheet(Sheet):
    method iterload (line 128) | def iterload(self):
  class RlimitsSheet (line 144) | class RlimitsSheet(Sheet):
    method soft (line 151) | def soft(self, r):
    method hard (line 153) | def hard(self, r):
    method set_soft (line 155) | def set_soft(self, r, v):
    method set_hard (line 157) | def set_hard(self, r, v):
    method iterload (line 160) | def iterload(self):
  function cpuStats (line 168) | def cpuStats(vd):
  function memStats (line 172) | def memStats(vd):
  function processes (line 176) | def processes(vd):
  function chooseSignal (line 185) | def chooseSignal(vd):

FILE: visidata/features/pypkg.py
  class PythonPackagesSheet (line 6) | class PythonPackagesSheet(PythonSheet):
    method reload (line 20) | def reload(self):
    method openRow (line 30) | def openRow(self, row):

FILE: visidata/features/rank.py
  class RankAggregator (line 6) | class RankAggregator(ListAggregator):
    method aggregate (line 11) | def aggregate(self, col, rows) -> [int]:
    method aggregate_list (line 14) | def aggregate_list(self, col, rows) -> [int]:
    method rank (line 19) | def rank(self, col, rows):
  function rank_sorted_iterable (line 55) | def rank_sorted_iterable(vals_sorted) -> [int]:
  function addcol_sheetrank (line 68) | def addcol_sheetrank(sheet, rows):

FILE: visidata/features/regex.py
  function help_regex (line 9) | def help_regex(vd):
  function makeRegexSplitter (line 16) | def makeRegexSplitter(vd, regex, origcol):
  function makeRegexMatcher (line 20) | def makeRegexMatcher(vd, regex, origcol):
  function RegexColumn (line 31) | def RegexColumn(vs, regexMaker, origcol, regexstr):
  function addRegexColumns (line 41) | def addRegexColumns(vs, regexMaker, origcol, regexstr):
  function regexTransform (line 78) | def regexTransform(vd, origcol, before='', after=''):
  function parse_sed_transform (line 83) | def parse_sed_transform(vd, instr):
  function indexWithEscape (line 91) | def indexWithEscape(s, char, escape_char='\\'):
  function setValuesFromRegex (line 105) | def setValuesFromRegex(sheet, cols, rows, before='', after=''):
  function inputRegex (line 117) | def inputRegex(vd, prompt, type='regex', **kwargs):
  function inputRegexSubst (line 123) | def inputRegexSubst(vd, prompt):

FILE: visidata/features/reload_every.py
  function reload_every (line 9) | def reload_every(sheet, seconds:int):
  function reload_modified (line 20) | def reload_modified(sheet):
  function reload_rows (line 37) | def reload_rows(self):

FILE: visidata/features/rename_col_cascade.py
  class Renamer (line 8) | class Renamer(ast.NodeTransformer):
    method __init__ (line 9) | def __init__(self, find, replace):
    method visit_Name (line 13) | def visit_Name(self, node):
  function setName (line 21) | def setName(col, newname):

FILE: visidata/features/repeat.py
  function prevCmdlogRow (line 5) | def prevCmdlogRow(vd):
  function repeat_last (line 13) | def repeat_last(sheet, cmdrow):
  function repeat_for_n (line 19) | def repeat_for_n(sheet, cmdrow, n=1):
  function repeat_for_selected (line 26) | def repeat_for_selected(sheet, cmdrow):

FILE: visidata/features/repl.py
  class _LazyNamespace (line 20) | class _LazyNamespace(dict):
    method __init__ (line 22) | def __init__(self, lcm):
    method __getitem__ (line 28) | def __getitem__(self, k):
    method __contains__ (line 35) | def __contains__(self, k):
  function openRepl (line 42) | def openRepl(vd):

FILE: visidata/features/replay_bulk.py
  function replayOne (line 13) | def replayOne(vd, r):
  function replay_reset (line 18) | def replay_reset(vs):  # noqa: ARG001
  function replay_end (line 31) | def replay_end(vs):  # noqa: ARG001
  function printStatus (line 39) | def printStatus(vd, *args, priority=0, source=None):
  function replay_output (line 52) | def replay_output(vs):
  function allow_error (line 62) | def allow_error(vs, pattern:str):  # noqa: ARG001
  function replay_exit (line 68) | def replay_exit(vs):  # noqa: ARG001

FILE: visidata/features/scroll_context.py
  function checkCursor (line 25) | def checkCursor(sheet):
  function toggle_scrollfix (line 48) | def toggle_scrollfix(sheet):

FILE: visidata/features/select_equal_selected.py
  function select_equal_selected (line 6) | def select_equal_selected(sheet, col):

FILE: visidata/features/setcol_fake.py
  function addFakerProviders (line 11) | def addFakerProviders(fake, providers):
  function setValuesFromFaker (line 37) | def setValuesFromFaker(col, faketype, rows):

FILE: visidata/features/slide.py
  function slide_col (line 7) | def slide_col(sheet, colidx, newcolidx):
  function slide_keycol (line 12) | def slide_keycol(sheet, fromKeyColIdx, toKeyColIdx):
  function slide_row (line 18) | def slide_row(sheet, rowidx, newcolidx):
  function moveKeyCol (line 23) | def moveKeyCol(sheet, fromKeyColIdx, toKeyColIdx):
  function moveVisibleCol (line 43) | def moveVisibleCol(sheet, fromVisColIdx, toVisColIdx):
  function make_tester (line 104) | def make_tester(setup_vdx):
  function test_slide_keycol_1 (line 116) | def test_slide_keycol_1(vd):
  function test_slide_leftmost (line 141) | def test_slide_leftmost(vd):

FILE: visidata/features/sparkline.py
  function sparkline (line 12) | def sparkline(*values):
  function addcol_sparkline (line 38) | def addcol_sparkline(sheet, sourceCols):

FILE: visidata/features/status_source.py
  function getStatusSource (line 7) | def getStatusSource(vd) -> str:

FILE: visidata/features/sysedit.py
  function syseditCells (line 6) | def syseditCells(sheet, cols, rows, filetype=None):
  function syseditCells_async (line 13) | def syseditCells_async(sheet, cols, rows, filetype=None):

FILE: visidata/features/sysopen_mailcap.py
  function run_mailcap (line 19) | def run_mailcap(sheet, p, key='view'):

FILE: visidata/features/term_extras.py
  function ansi (line 7) | def ansi(*args):
  function set_titlebar (line 12) | def set_titlebar(vd, title:str):

FILE: visidata/features/transpose.py
  class TransposeSheet (line 5) | class TransposeSheet(Sheet):
    method beforeLoad (line 6) | def beforeLoad(self):
    method loader (line 15) | def loader(self):

FILE: visidata/features/type_ipaddr.py
  function isSupernet (line 14) | def isSupernet(cell, network, isNull):
  function selectSupernets (line 31) | def selectSupernets(col, ip):

FILE: visidata/features/type_url.py
  function displayer_url (line 5) | def displayer_url(self, dw:DisplayWrapper, width=None):

FILE: visidata/features/unfurl.py
  class UnfurledSheet (line 17) | class UnfurledSheet(Sheet):
    method resetCols (line 19) | def resetCols(self):
    method iterload (line 32) | def iterload(self):
  function unfurl_col (line 65) | def unfurl_col(sheet, col):

FILE: visidata/features/window.py
  function window (line 7) | def window(sheet, before:int=0, after:int=0):
  function window (line 16) | def window(col, before:int=0, after:int=0):
  class WindowColumn (line 22) | class WindowColumn(Column):
    method getValue (line 23) | def getValue(self, row):
    method _calcWindowRows (line 27) | def _calcWindowRows(self, outvals):
    method windowrows (line 32) | def windowrows(self):
    method __getstate__ (line 39) | def __getstate__(self):
    method __setstate__ (line 46) | def __setstate__(self, r):
  function addcol_window (line 51) | def addcol_window(sheet, curcol):
  function select_around (line 59) | def select_around(sheet, n):

FILE: visidata/form.py
  function open_mnu (line 6) | def open_mnu(vd, p):
  class FormSheet (line 12) | class FormSheet(VisiDataMetaSheet):
  function replayCommand (line 16) | def replayCommand(vd, longname, input=None, sheet=None, col='', row=''):
  class FormCanvas (line 20) | class FormCanvas(BaseSheet):
    method onPressed (line 24) | def onPressed(self, r):
    method onReleased (line 28) | def onReleased(self, r):
    method reload (line 32) | def reload(self):
    method draw (line 35) | def draw(self, scr):
    method run (line 60) | def run(self, scr):
  function confirm (line 104) | def confirm(vd, prompt, exc=EscapeException):

FILE: visidata/freqtbl.py
  function valueNames (line 14) | def valueNames(vd, discrete_vals, numeric_vals):
  class HistogramColumn (line 21) | class HistogramColumn(Column):
    method calcValue (line 23) | def calcValue(col, row):
    method updateLargest (line 28) | def updateLargest(col, row):
  function makeFreqTable (line 32) | def makeFreqTable(sheet, *groupByCols):
  class FreqTableSheet (line 41) | class FreqTableSheet(PivotSheet):
    method groupByColsName (line 61) | def groupByColsName(self):
    method selectRow (line 64) | def selectRow(self, row):
    method unselectRow (line 69) | def unselectRow(self, row):
    method addUndoSelection (line 73) | def addUndoSelection(self):
    method select (line 82) | def select(self, rows, status=True, progress=True, add_undo=True):
    method unselect (line 87) | def unselect(self, rows, status=True, progress=True, add_undo=True):
    method toggle (line 92) | def toggle(self, rows, add_undo=True):
    method select_row (line 98) | def select_row(self, row, add_undo=True):
    method unselect_row (line 104) | def unselect_row(self, row, add_undo=True):
    method toggle_row (line 110) | def toggle_row(self, row, add_undo=True):
    method resetCols (line 116) | def resetCols(self):
    method loader (line 135) | def loader(self):
    method afterLoad (line 142) | def afterLoad(self):
    method openRow (line 148) | def openRow(self, row):
    method openRows (line 157) | def openRows(self, rows):
    method openCell (line 163) | def openCell(self, col, row):
  class FreqTableSheetSummary (line 167) | class FreqTableSheetSummary(FreqTableSheet):
    method afterLoad (line 169) | def afterLoad(self):
  function makeFreqTableSheetSummary (line 174) | def makeFreqTableSheetSummary(sheet, *groupByCols):
  class FreqTablePreviewSheet (line 182) | class FreqTablePreviewSheet(Sheet):
    method rows (line 184) | def rows(self):
  function exceptRows (line 189) | def exceptRows(sheet, exceptrows):

FILE: visidata/fuzzymatch.py
  function asciiFuzzyIndex (line 78) | def asciiFuzzyIndex(target, pattern):
  function charClassOfAscii (line 97) | def charClassOfAscii(char):
  function bonusFor (line 111) | def bonusFor(prevClass, class_):
  function debugV2 (line 137) | def debugV2(T, pattern, F, lastIdx, H, C):
  class MatchResult (line 167) | class MatchResult:
  function _fuzzymatch (line 184) | def _fuzzymatch(target: str, pattern: str) -> MatchResult:
  function _format_match (line 359) | def _format_match(s, positions):
  function fuzzymatch (line 377) | def fuzzymatch(vd, haystack:"list[dict[str, str]]", needles:"list[str]) ...
  function test_fuzzymatch (line 406) | def test_fuzzymatch(vd):

FILE: visidata/graph.py
  function numericCols (line 17) | def numericCols(vd, cols):
  class InvertedCanvas (line 21) | class InvertedCanvas(Canvas):
    method render_async (line 23) | def render_async(self):
    method fixPoint (line 26) | def fixPoint(self, plotterPoint, canvasPoint):
    method rowsWithin (line 32) | def rowsWithin(self, plotter_bbox):
    method zoomTo (line 35) | def zoomTo(self, bbox):
    method scaleY (line 41) | def scaleY(self, canvasY) -> int:
    method unscaleY (line 45) | def unscaleY(self, plotterY_inverted):
    method canvasMouse (line 50) | def canvasMouse(self):
    method calcTopCursorY (line 56) | def calcTopCursorY(self):
    method calcBottomCursorY (line 60) | def calcBottomCursorY(self):
    method startCursor (line 66) | def startCursor(self):
  class GraphSheet (line 74) | class GraphSheet(InvertedCanvas):
    method __init__ (line 77) | def __init__(self, *names, **kwargs):
    method resetCanvasDimensions (line 89) | def resetCanvasDimensions(self, windowHeight, windowWidth):
    method reload (line 95) | def reload(self):
    method draw (line 129) | def draw(self, scr):
    method draw_reflines (line 142) | def draw_reflines(self, scr):
    method resetBounds (line 162) | def resetBounds(self, refresh=True):
    method moveToRow (line 168) | def moveToRow(self, rowstr):
    method plot_elements (line 174) | def plot_elements(self, invert_y=True):
    method plot_reflines (line 178) | def plot_reflines(self):
    method moveToCol (line 211) | def moveToCol(self, colstr):
    method formatX (line 217) | def formatX(self, amt):
    method formatY (line 220) | def formatY(self, amt):
    method formatXLabel (line 224) | def formatXLabel(self, amt):
    method formatYLabel (line 237) | def formatYLabel(self, amt):
    method parseX (line 244) | def parseX(self, txt):
    method parseY (line 247) | def parseY(self, txt):
    method add_y_axis_label (line 250) | def add_y_axis_label(self, frac):
    method add_x_axis_label (line 261) | def add_x_axis_label(self, frac):
    method createLabels (line 284) | def createLabels(self):
    method rowsWithin (line 309) | def rowsWithin(self, plotter_bbox):
    method draw_refline_x (line 314) | def draw_refline_x(self):
    method draw_refline_y (line 327) | def draw_refline_y(self):
    method erase_refline_x (line 336) | def erase_refline_x(self):
    method erase_refline_y (line 350) | def erase_refline_y(self):
  function format_input_value (line 363) | def format_input_value(val, type):
  function set_y (line 391) | def set_y(sheet, s):
  function set_x (line 397) | def set_x(sheet, s):
  function reload (line 413) | def reload(sheet):

FILE: visidata/guide.py
  function addGuide (line 84) | def addGuide(vd, name):
  class GuideIndex (line 90) | class GuideIndex(Sheet):
    method iterload (line 102) | def iterload(self):
    method openRow (line 112) | def openRow(self, row):
  class OptionHelpGetter (line 116) | class OptionHelpGetter:
    method __getattr__ (line 118) | def __getattr__(self, optname):
  class CommandHelpGetter (line 123) | class CommandHelpGetter:
    method __init__ (line 125) | def __init__(self, cls):
    method __getattr__ (line 130) | def __getattr__(self, k):
    method __getitem__ (line 133) | def __getitem__(self, k):
  class GuideSheet (line 157) | class GuideSheet(Sheet):
    method iterload (line 167) | def iterload(self):
  function getGuide (line 200) | def getGuide(vd, name): # -> GuideSheet()
  function inputKeys (line 208) | def inputKeys(vd, prompt):
  function getCommandInfo (line 217) | def getCommandInfo(sheet, keys):

FILE: visidata/help.py
  function wantsHelp (line 22) | def wantsHelp(vd, feat):
  function hint_basichelp (line 27) | def hint_basichelp(sheet):
  function iterMenuPaths (line 32) | def iterMenuPaths(vd, item=None, menupath=[]):
  function menuPathsByLongname (line 48) | def menuPathsByLongname(vd):
  class HelpSheet (line 53) | class HelpSheet(MetaSheet):
    method iterload (line 72) | def iterload(self):
    method revbinds (line 92) | def revbinds(self):
  class HelpPane (line 101) | class HelpPane:
    method __init__ (line 102) | def __init__(self, name):
    method width (line 110) | def width(self):
    method height (line 114) | def height(self):
    method draw (line 117) | def draw(self, scr, x=None, y=None, **kwargs):
  function getHelpPane (line 163) | def getHelpPane(vd, name, module='visidata') -> HelpPane:
  function openManPage (line 178) | def openManPage(vd):

FILE: visidata/hint.py
  function prevHints (line 7) | def prevHints(sheet):
  function getHint (line 12) | def getHint(sheet, *args, **kwargs) -> str:

FILE: visidata/indexsheet.py
  class IndexSheet (line 4) | class IndexSheet(Sheet):
    method newRow (line 24) | def newRow(self):
    method openRow (line 27) | def openRow(self, row):
    method getSheet (line 30) | def getSheet(self, k):
    method addRow (line 35) | def addRow(self, sheet, **kwargs):
    method reloadSheets (line 41) | def reloadSheets(self, sheets):
  class SheetsSheet (line 46) | class SheetsSheet(IndexSheet):
    method reload (line 63) | def reload(self):
    method sort (line 66) | def sort(self):
  class GlobalSheetsSheet (line 70) | class GlobalSheetsSheet(SheetsSheet):  #1620
    method sort (line 71) | def sort(self):
  function sheetsSheet (line 76) | def sheetsSheet(vd):
  function allSheetsSheet (line 81) | def allSheetsSheet(vd):
  function nextRow (line 87) | def nextRow(sheet, n=1):

FILE: visidata/input_history.py
  function addInputHistory (line 9) | def addInputHistory(vd, input:str, type:str=''):
  function processInputHistory (line 16) | def processInputHistory(vd, input:str, type:str=''):
  class InputHistorySheet (line 31) | class InputHistorySheet(Sheet):
    method iterload (line 38) | def iterload(self):
  function run (line 44) | def run(vd, *args, **kwargs):
  function inputHistorySheet (line 51) | def inputHistorySheet(vd):

FILE: visidata/keys.py
  function prettykeys (line 114) | def prettykeys(vd, key:str) -> str:

FILE: visidata/loaders/_pandas.py
  function open_pandas (line 6) | def open_pandas(vd, p):
  function open_dta (line 10) | def open_dta(vd, p):
  function save_dta (line 22) | def save_dta(vd, p, *sheets):
  class DataFrameAdapter (line 55) | class DataFrameAdapter:
    method __init__ (line 56) | def __init__(self, df):
    method __len__ (line 63) | def __len__(self):
    method __getitem__ (line 68) | def __getitem__(self, k):
    method __getattr__ (line 73) | def __getattr__(self, k):
  class PandasSheet (line 79) | class PandasSheet(Sheet):
    method dtype_to_type (line 93) | def dtype_to_type(self, dtype):
    method read_tsv (line 109) | def read_tsv(self, path, **kwargs):
    method df (line 115) | def df(self):
    method df (line 120) | def df(self, val):
    method getValue (line 126) | def getValue(self, col, row):
    method setValue (line 130) | def setValue(self, col, row, val):
    method reload (line 147) | def reload(self):
    method sort (line 204) | def sort(self):
    method _checkSelectedIndex (line 213) | def _checkSelectedIndex(self):
    method rowid (line 221) | def rowid(self, row):
    method isSelected (line 228) | def isSelected(self, row):
    method selectRow (line 234) | def selectRow(self, row):
    method unselectRow (line 239) | def unselectRow(self, row):
    method nSelectedRows (line 246) | def nSelectedRows(self):
    method selectedRows (line 251) | def selectedRows(self):
    method select (line 257) | def select(self, rows, status=True, progress=True):
    method unselect (line 263) | def unselect(self, rows, status=True, progress=True):
    method clearSelected (line 268) | def clearSelected(self):
    method selectByIndex (line 272) | def selectByIndex(self, start=None, end=None):
    method unselectByIndex (line 276) | def unselectByIndex(self, start=None, end=None):
    method toggleByIndex (line 280) | def toggleByIndex(self, start=None, end=None):
    method _selectByILoc (line 285) | def _selectByILoc(self, mask, selected=True):
    method selectByRegex (line 290) | def selectByRegex(self, regex, columns, unselect=False):
    method addUndoSelection (line 307) | def addUndoSelection(self):
    method nRows (line 311) | def nRows(self):
    method newRows (line 316) | def newRows(self, n):
    method addRows (line 328) | def addRows(self, rows, index=None, undo=True):
    method _deleteRows (line 340) | def _deleteRows(self, which):
    method addRow (line 346) | def addRow(self, row, index=None):
    method delete_row (line 350) | def delete_row(self, rowidx):
    method deleteBy (line 364) | def deleteBy(self, by):
    method deleteSelected (line 377) | def deleteSelected(self):
  function view_pandas (line 383) | def view_pandas(vd, df):

FILE: visidata/loaders/api_airtable.py
  function guessurl_airtable (line 12) | def guessurl_airtable(vd, p, response):
  function open_airtable (line 19) | def open_airtable(vd, p):
  class AirtableSheet (line 38) | class AirtableSheet(Sheet):
    method iterload (line 50) | def iterload(self):
    method newRow (line 65) | def newRow(self):
  function api (line 70) | def api(self):

FILE: visidata/loaders/api_matrix.py
  function openhttp_matrix (line 20) | def openhttp_matrix(vd, p):
  class MatrixRoomsSheet (line 40) | class MatrixRoomsSheet(Sheet):
    method iterload (line 41) | def iterload(self):
  class MatrixSheet (line 45) | class MatrixSheet(Sheet):
    method sourcename (line 60) | def sourcename(self):
    method reload (line 68) | def reload(self):
    method add_room (line 94) | def add_room(self, room):
    method get_room_messages (line 100) | def get_room_messages(self, room):
    method addRow (line 111) | def addRow(self, r, **kwargs):
    method global_event (line 117) | def global_event(self, chunk):
    method room_event (line 120) | def room_event(self, room, chunk):
    method add_message (line 138) | def add_message(self, text):
    method openRow (line 141) | def openRow(self, row):

FILE: visidata/loaders/api_reddit.py
  function open_reddit (line 22) | def open_reddit(vd, p):
  function reddit (line 40) | def reddit(vd):
  function hiddenCols (line 122) | def hiddenCols(hidden_attrs):
  class SubredditSheet (line 132) | class SubredditSheet(Sheet):
    method iterload (line 148) | def iterload(self):
    method openRow (line 161) | def openRow(self, row):
    method openRows (line 164) | def openRows(self, rows):
  class RedditorsSheet (line 169) | class RedditorsSheet(Sheet):
    method iterload (line 181) | def iterload(self):
    method openRow (line 188) | def openRow(self, row):
    method openRows (line 191) | def openRows(self, rows):
  class RedditSubmissions (line 196) | class RedditSubmissions(Sheet):
    method iterload (line 219) | def iterload(self):
    method openRow (line 225) | def openRow(self, row):
  class RedditComments (line 229) | class RedditComments(Sheet):
    method iterload (line 246) | def iterload(self):
    method openRow (line 249) | def openRow(self, row):
  class RedditGuide (line 253) | class RedditGuide(RedditSubmissions):
  function addRowsFromQuery (line 276) | def addRowsFromQuery(sheet, q):
  function addRowsFromQuery (line 283) | def addRowsFromQuery(sheet, q):
  function sysopen_subreddits (line 289) | def sysopen_subreddits(vd, *subreddits):

FILE: visidata/loaders/api_zulip.py
  function open_zulip (line 14) | def open_zulip(vd, p):
  function z_rpc (line 35) | def z_rpc(vd, r, result_field_name=None):
  function allStreams (line 43) | def allStreams(vd):
  function subscribedStreams (line 48) | def subscribedStreams(vd):
  function allMessages (line 53) | def allMessages(vd):
  function parseColumns (line 58) | def parseColumns(vd, fieldlist):
  class ZulipAPISheet (line 70) | class ZulipAPISheet(Sheet):
    method iterload (line 77) | def iterload(self):
    method addRow (line 91) | def addRow(self, r, **kwargs):
  class ZulipStreamsSheet (line 95) | class ZulipStreamsSheet(ZulipAPISheet):
    method openRow (line 104) | def openRow(self, r):
    method openCell (line 107) | def openCell(self, c, r):
  class ZulipTopicsSheet (line 114) | class ZulipTopicsSheet(ZulipAPISheet):
    method openRow (line 117) | def openRow(self, r):
  class ZulipMembersSheet (line 121) | class ZulipMembersSheet(ZulipAPISheet):
    method openRow (line 127) | def openRow(self, r):
  class ZulipMessagesSheet (line 131) | class ZulipMessagesSheet(Sheet):
    method reload (line 155) | def reload(self):
    method get_channel_name (line 181) | def get_channel_name(self, r):
    method update_message (line 188) | def update_message(self, msgid, content):
    method openRow (line 195) | def openRow(self, r):
    method received_event (line 200) | def received_event(self, event):
    method reply_message (line 204) | def reply_message(self, msg, row):
    method send_message (line 212) | def send_message(self, msg, subject, dest, msgtype='stream'):

FILE: visidata/loaders/archive.py
  function guess_zip (line 13) | def guess_zip(vd, p):
  function guess_tar (line 18) | def guess_tar(vd, p):
  function open_zip (line 26) | def open_zip(vd, p):
  function open_tar (line 30) | def open_tar(vd, p):
  class ZipSheet (line 39) | class ZipSheet(Sheet):
    method openZipFile (line 68) | def openZipFile(self, fp, *args, **kwargs):
    method openRow (line 78) | def openRow(self, row):
    method extract (line 83) | def extract(self, *rows, path=None):
    method sysopen_row (line 92) | def sysopen_row(self, row):
    method extract_async (line 100) | def extract_async(self, *rows, path=None):
    method zfp (line 107) | def zfp(self):
    method iterload (line 124) | def iterload(self):
  class TarSheet (line 147) | class TarSheet(Sheet):
    method openRow (line 162) | def openRow(self, fi):
    method iterload (line 166) | def iterload(self):

FILE: visidata/loaders/arrow.py
  function open_arrow (line 8) | def open_arrow(vd, p):
  function open_arrows (line 14) | def open_arrows(vd, p):
  function arrow_to_vdtype (line 19) | def arrow_to_vdtype(t):
  class ArrowSheet (line 59) | class ArrowSheet(Sheet):
    method iterload (line 60) | def iterload(self):
  function save_arrow (line 83) | def save_arrow(vd, p, sheet, streaming=False):
  function save_arrows (line 125) | def save_arrows(vd, p, sheet):

FILE: visidata/loaders/claude.py
  function _ts (line 18) | def _ts(s):
  function _content_text (line 30) | def _content_text(msg):
  function open_claude (line 51) | def open_claude(vd, p):
  class ClaudeProjectsSheet (line 59) | class ClaudeProjectsSheet(Sheet):
    method iterload (line 67) | def iterload(self):
    method openRow (line 74) | def openRow(self, row):
  function _session_meta (line 78) | def _session_meta(p):
  class ClaudeSessionsSheet (line 102) | class ClaudeSessionsSheet(Sheet):
    method iterload (line 112) | def iterload(self):
    method openRow (line 117) | def openRow(self, row):
  class ClaudeSessionSheet (line 122) | class ClaudeSessionSheet(Sheet):
    method iterload (line 132) | def iterload(self):

FILE: visidata/loaders/conll.py
  function open_conll (line 6) | def open_conll(vd, p):
  function open_conllu (line 11) | def open_conllu(vd, p):
  class ConllSheet (line 15) | class ConllSheet(TableSheet):
    method iterload (line 38) | def iterload(self):

FILE: visidata/loaders/csv.py
  function guess_csv_delimiter (line 16) | def guess_csv_delimiter(vd, p):
  function guess_csv (line 23) | def guess_csv(vd, p):
  function open_csv (line 45) | def open_csv(vd, p):
  function removeNulls (line 48) | def removeNulls(fp):
  class CsvSheet (line 52) | class CsvSheet(SequenceSheet):
    method iterload (line 55) | def iterload(self):
  function save_csv (line 83) | def save_csv(vd, p, sheet):

FILE: visidata/loaders/eml.py
  function open_eml (line 6) | def open_eml(vd, p):
  class EmailSheet (line 11) | class EmailSheet(TableSheet):
    method iterload (line 18) | def iterload(self):
  function extract_part (line 25) | def extract_part(sheet, givenpath, part):
  function extract_parts (line 34) | def extract_parts(sheet, givenpath, *parts):

FILE: visidata/loaders/f5log.py
  class hexint (line 38) | class hexint(int):
    method __new__ (line 39) | def __new__(cls, value, *args, **kwargs):
    method __str__ (line 42) | def __str__(self):
  class delta_t (line 46) | class delta_t(int):
    method __new__ (line 47) | def __new__(cls, value, *args, **kwargs):
  class F5LogSheet (line 84) | class F5LogSheet(Sheet):
    class F5LogRow (line 85) | class F5LogRow:
      method __init__ (line 86) | def __init__(
      method __getattr__ (line 114) | def __getattr__(self, item):
    method colorizeMonitors (line 230) | def colorizeMonitors(sheet, col: Column, row: F5LogRow, value):
    method colorizeRows (line 268) | def colorizeRows(sheet, col: Column, row: F5LogRow, value):
    method split_audit_bigip_tmsh_audit (line 280) | def split_audit_bigip_tmsh_audit(msg):
    method split_audit_scriptd_run_script (line 288) | def split_audit_scriptd_run_script(msg):
    method split_audit_mcpd_mcp_error (line 296) | def split_audit_mcpd_mcp_error(msg):
    method split_ltm_pool_mon_status (line 354) | def split_ltm_pool_mon_status(msg):
    method split_ltm_poolnode_mon_abled (line 406) | def split_ltm_poolnode_mon_abled(msg):
    method split_ltm_pool_has_no_avail_mem (line 419) | def split_ltm_pool_has_no_avail_mem(msg):
    method split_ltm_pool_has_avail_mem (line 428) | def split_ltm_pool_has_avail_mem(msg):
    method split_ltm_rule (line 437) | def split_ltm_rule(msg):
    method split_ltm_rule_missing_datagroup (line 472) | def split_ltm_rule_missing_datagroup(msg):
    method split_ltm_cert_expiry (line 494) | def split_ltm_cert_expiry(msg):
    method split_ltm_connection_error (line 513) | def split_ltm_connection_error(msg):
    method split_ltm_virtual_status (line 527) | def split_ltm_virtual_status(msg):
    method split_ltm_virtual_address_status_or_irule_profile_err (line 545) | def split_ltm_virtual_address_status_or_irule_profile_err(msg):
    method split_ltm_ssl_handshake_fail (line 566) | def split_ltm_ssl_handshake_fail(msg):
    method split_ltm_shared_ciphers (line 588) | def split_ltm_shared_ciphers(msg):
    method split_ltm_rst_reason (line 601) | def split_ltm_rst_reason(msg):
    method split_ltm_inet_port_exhaust (line 628) | def split_ltm_inet_port_exhaust(msg):
    method split_ltm_conn_limit_reached (line 645) | def split_ltm_conn_limit_reached(msg):
    method split_ltm_syncookie_threshold (line 670) | def split_ltm_syncookie_threshold(msg):
    method split_ltm_sweeper_active2 (line 688) | def split_ltm_sweeper_active2(msg):
    method split_ltm_sweeper_active3 (line 699) | def split_ltm_sweeper_active3(msg):
    method split_ltm_dns_failed_xfr_rcode (line 709) | def split_ltm_dns_failed_xfr_rcode(msg):
    method split_ltm_dns_failed_rr (line 717) | def split_ltm_dns_failed_rr(msg):
    method split_ltm_dns_failed_xfr (line 725) | def split_ltm_dns_failed_xfr(msg):
    method split_ltm_dns_handling_notify (line 735) | def split_ltm_dns_handling_notify(msg):
    method split_ltm_dns_axfr_succeeded_1f (line 743) | def split_ltm_dns_axfr_succeeded_1f(msg):
    method split_ltm_dns_axfr_succeeded_2c (line 752) | def split_ltm_dns_axfr_succeeded_2c(msg):
    method split_ltm_dns_ignoring_tfer (line 762) | def split_ltm_dns_ignoring_tfer(msg):
    method split_ltm_http_process_state (line 772) | def split_ltm_http_process_state(msg):
    method split_ltm_http_header_exceeded (line 836) | def split_ltm_http_header_exceeded(msg):
    method split_gtm_monitor (line 858) | def split_gtm_monitor(msg):
    method split_gtm_monitor_instance (line 897) | def split_gtm_monitor_instance(msg):
    method split_gtm_syncgroup_change (line 922) | def split_gtm_syncgroup_change(msg):
    method split_gtm_changed_state (line 932) | def split_gtm_changed_state(msg):
    method split_tmm_address_conflict (line 941) | def split_tmm_address_conflict(msg):
    method __init__ (line 1063) | def __init__(self, *args, **kwargs):
    method iterload (line 1082) | def iterload(self):
  function open_f5log (line 1202) | def open_f5log(vd: VisiData, p: Path) -> Sheet:

FILE: visidata/loaders/fec.py
  class DiveSheet (line 50) | class DiveSheet(Sheet):
    method reload (line 53) | def reload(self):
    method openRow (line 112) | def openRow(self, row):
  class FECItemizationSheet (line 129) | class FECItemizationSheet(Sheet):
    method reload (line 135) | def reload(self):
    method set_columns_from_row (line 147) | def set_columns_from_row(self, row):
    method openRow (line 152) | def openRow(self, row):
  class FECScheduleSheet (line 155) | class FECScheduleSheet(Sheet):
    method reload (line 169) | def reload(self):
    method openRow (line 181) | def openRow(self, row):
  class FECFiling (line 192) | class FECFiling(Sheet):
    method reload (line 207) | def reload(self):
    method openRow (line 289) | def openRow(self, row):
  function open_fec (line 293) | def open_fec(vd, p):

FILE: visidata/loaders/fixed_width.py
  function open_fixed (line 10) | def open_fixed(vd, p):
  function getMaxDataWidth (line 14) | def getMaxDataWidth(col, rows):  #2255 need real max width for fixed wid...
  class FixedWidthColumn (line 29) | class FixedWidthColumn(WritableColumn):
    method __init__ (line 30) | def __init__(self, name, i, j, **kwargs):
    method calcValue (line 34) | def calcValue(self, row):
    method putValue (line 37) | def putValue(self, row, value):
  function columnize (line 46) | def columnize(rows, has_header=True):
  class FixedWidthColumnsSheet (line 95) | class FixedWidthColumnsSheet(SequenceSheet):
    method addRow (line 97) | def addRow(self, row, index=None):
    method iterload (line 100) | def iterload(self):
    method setCols (line 120) | def setCols(self, headerlines):
  function save_fixed (line 125) | def save_fixed(vd, p, *vsheets):

FILE: visidata/loaders/frictionless.py
  function open_frictionless (line 4) | def open_frictionless(vd, p):
  class FrictionlessIndexSheet (line 7) | class FrictionlessIndexSheet(IndexSheet):
    method iterload (line 8) | def iterload(self):

FILE: visidata/loaders/geojson.py
  function open_geojson (line 11) | def open_geojson(vd, p):
  class GeoJSONColumn (line 14) | class GeoJSONColumn(WritableColumn):
    method calcValue (line 15) | def calcValue(self, row):
    method putValue (line 18) | def putValue(self, row, val):
  class GeoJSONSheet (line 23) | class GeoJSONSheet(PythonSheet):
    method iterload (line 27) | def iterload(self):
  class GeoJSONMap (line 51) | class GeoJSONMap(InvertedCanvas):
    method reload (line 56) | def reload(self):
    method parse_geometry (line 81) | def parse_geometry(self, row, colour, bbox=None):
  function reduce_coords (line 115) | def reduce_coords(coords, initial):
  function _rowdict (line 120) | def _rowdict(cols, row):
  function save_geojson (line 135) | def save_geojson(vd, p, vs):

FILE: visidata/loaders/google.py
  function _google_creds_fn (line 6) | def _google_creds_fn():
  function google_auth (line 19) | def google_auth(vd, scopes=None):

FILE: visidata/loaders/graphviz.py
  function is_valid (line 7) | def is_valid(v):
  function save_dot (line 16) | def save_dot(vd, p, vs):

FILE: visidata/loaders/grep.py
  function open_grep (line 9) | def open_grep(vd, p):
  function save_grep (line 13) | def save_grep(vd, p, *vsheets):
  function format_row (line 16) | def format_row(rowdict):
  class GrepSheet (line 33) | class GrepSheet(JsonSheet):
    method iterload (line 46) | def iterload(self):
    method afterLoad (line 92) | def afterLoad(self):
  function sysopen_row (line 97) | def sysopen_row(sheet, row):

FILE: visidata/loaders/hdf5.py
  function open_h5 (line 5) | def open_h5(vd, p):
  class Hdf5ObjSheet (line 12) | class Hdf5ObjSheet(Sheet):
    method iterload (line 15) | def iterload(self):
    method openRow (line 68) | def openRow(self, row):
  function _guess_type (line 80) | def _guess_type(fmt):

FILE: visidata/loaders/html.py
  function guess_html (line 13) | def guess_html(vd, p):
  function open_html (line 25) | def open_html(vd, p):
  class HtmlTablesSheet (line 31) | class HtmlTablesSheet(IndexSheet):
    method iterload (line 43) | def iterload(self):
  function is_header (line 59) | def is_header(elem):
  class HtmlLinksSheet (line 68) | class HtmlLinksSheet(Sheet):
    method iterload (line 77) | def iterload(self):
    method openRow (line 84) | def openRow(self, row):
  class HtmlElementsSheet (line 87) | class HtmlElementsSheet(Sheet):
    method iterload (line 92) | def iterload(self):
    method openRow (line 103) | def openRow(self, row):
  class HtmlTableSheet (line 107) | class HtmlTableSheet(Sheet):
    method iterload (line 111) | def iterload(self):
  function save_html (line 213) | def save_html(vd, p, *vsheets):
  function utf8_parser (line 245) | def utf8_parser(vd):
  function HTML (line 252) | def HTML(vd, s):

FILE: visidata/loaders/http.py
  function guessurl_mimetype (line 12) | def guessurl_mimetype(vd, path, response):
  function openurl_http (line 30) | def openurl_http(vd, path, filetype=None):
  function parse_header_links (line 99) | def parse_header_links(link_header):

FILE: visidata/loaders/imap.py
  function openurl_imap (line 7) | def openurl_imap(vd, url, **kwargs):
  class ImapSheet (line 12) | class ImapSheet(TableSheet):
    method iterload (line 26) | def iterload(self):
    method addRow (line 65) | def addRow(self, row, **kwargs):

FILE: visidata/loaders/jrnl.py
  function open_jrnl (line 10) | def open_jrnl(vd, p):
  class JrnlSheet (line 14) | class JrnlSheet(TableSheet):
    method iterload (line 23) | def iterload(self):
  function save_jrnl (line 44) | def save_jrnl(vd, p, *vsheets):

FILE: visidata/loaders/json.py
  function guess_json (line 12) | def guess_json(vd, p):
  function open_jsonobj (line 29) | def open_jsonobj(vd, p):
  function open_jsonl (line 33) | def open_jsonl(vd, p):
  class JsonSheet (line 39) | class JsonSheet(Sheet):
    method resetCols (line 41) | def resetCols(self):
    method iterload (line 45) | def iterload(self):
    method addColumn (line 71) | def addColumn(self, *cols, index=None):
    method addRow (line 76) | def addRow(self, row, index=None):
    method newRow (line 93) | def newRow(self, **fields):
    method openRow (line 96) | def openRow(self, row):
  class _vjsonEncoder (line 103) | class _vjsonEncoder(json.JSONEncoder):
    method default (line 104) | def default(self, obj):
  function get_json_value (line 109) | def get_json_value(vd, col, row):
  function _rowdict (line 120) | def _rowdict(cols, row, keep_nulls=False):
  function encode_json (line 130) | def encode_json(vd, row, cols, enc=_vjsonEncoder(sort_keys=False)):
  function save_json (line 136) | def save_json(vd, p, *vsheets):
  function write_jsonl (line 171) | def write_jsonl(vs, fp):
  function save_jsonl (line 191) | def save_jsonl(vd, p, *vsheets):
  function JSON (line 200) | def JSON(vd, s:str):
  function find_duplicates (line 204) | def find_duplicates(names):

FILE: visidata/loaders/jsonla.py
  function guess_jsonla (line 7) | def guess_jsonla(vd, p):
  function open_jsonla (line 36) | def open_jsonla(vd, p):
  class JsonlArraySheet (line 40) | class JsonlArraySheet(SequenceSheet):
    method iterload (line 42) | def iterload(self):
  function get_jsonla_rows (line 52) | def get_jsonla_rows(sheet, cols):
  class _vjsonEncoder (line 57) | class _vjsonEncoder(json.JSONEncoder):
    method default (line 58) | def default(self, obj):
  function write_jsonla (line 62) | def write_jsonla(vs, fp):
  function save_jsonla (line 74) | def save_jsonla(vd, p, *vsheets):

FILE: visidata/loaders/lsv.py
  function open_lsv (line 9) | def open_lsv(vd, p):
  function save_lsv (line 14) | def save_lsv(vd, p, *vsheets):
  class LsvSheet (line 23) | class LsvSheet(Sheet):
    method addRow (line 24) | def addRow(self, row, **kwargs):
    method iterload (line 32) | def iterload(self):

FILE: visidata/loaders/mailbox.py
  function open_mbox (line 5) | def open_mbox(vd, p):
  function open_maildir (line 10) | def open_maildir(vd, p):
  function open_mmdf (line 14) | def open_mmdf(vd, p):
  function open_babyl (line 18) | def open_babyl(vd, p):
  function open_mh (line 22) | def open_mh(vd, p):
  class MboxSheet (line 26) | class MboxSheet(Sheet):
    method iterload (line 36) | def iterload(self):

FILE: visidata/loaders/markdown.py
  function markdown_link (line 3) | def markdown_link(s, href):
  function markdown_escape (line 9) | def markdown_escape(s, style='orgmode'):
  function markdown_colhdr (line 21) | def markdown_colhdr(col):
  function write_md (line 27) | def write_md(p, *vsheets, md_style='orgmode'):
  function save_md (line 68) | def save_md(vd, p, *sheets):
  function save_jira (line 73) | def save_jira(vd, p, *sheets):

FILE: visidata/loaders/mbtiles.py
  function open_pbf (line 7) | def open_pbf(vd, p):
  function open_mbtiles (line 11) | def open_mbtiles(vd, p):
  function getListDepth (line 14) | def getListDepth(L):
  function getFeatures (line 21) | def getFeatures(tile_data):
  function tilename (line 27) | def tilename(row):
  class MbtilesSheet (line 31) | class MbtilesSheet(Sheet):
    method getTile (line 38) | def getTile(self, zoom_level, tile_col, tile_row):
    method iterload (line 50) | def iterload(self):
    method getPlot (line 58) | def getPlot(self, *rows):
    method openRow (line 67) | def openRow(self, row):
  class PbfSheet (line 72) | class PbfSheet(Sheet):
    method iterload (line 81) | def iterload(self):
  class PbfCanvas (line 91) | class PbfCanvas(InvertedCanvas):
    method iterpolylines (line 93) | def iterpolylines(self, r):
    method reload (line 118) | def reload(self):

FILE: visidata/loaders/msgpack.py
  function open_msgpack (line 5) | def open_msgpack(vd, p):
  class MsgpackSheet (line 12) | class MsgpackSheet(JsonSheet):
    method iterload (line 13) | def iterload(self):

FILE: visidata/loaders/mysql.py
  function codeToType (line 6) | def codeToType(type_code, colname):
  function openurl_mysql (line 24) | def openurl_mysql(vd, url, filetype=None):
  class SQL (line 30) | class SQL:
    method __init__ (line 31) | def __init__(self, url):
    method cur (line 35) | def cur(self, qstr):
    method query_async (line 64) | def query_async(self, qstr, callback=None):
  function cursorToColumns (line 69) | def cursorToColumns(cur, sheet):
  class MyTablesSheet (line 77) | class MyTablesSheet(Sheet):
    method iterload (line 80) | def iterload(self):
    method openRow (line 115) | def openRow(self, row):
  class MyTable (line 120) | class MyTable(Sheet):
    method iterload (line 121) | def iterload(self):

FILE: visidata/loaders/npy.py
  function open_npy (line 7) | def open_npy(vd, p):
  function open_npz (line 11) | def open_npz(vd, p):
  class NpySheet (line 17) | class NpySheet(Sheet):
    method iterload (line 18) | def iterload(self):
    method reloadCols (line 37) | def reloadCols(self):
  function _guess_type (line 62) | def _guess_type(shape, fmt):
  class NpzSheet (line 73) | class NpzSheet(vd.ZipSheet):
    method iterload (line 80) | def iterload(self):
    method openRow (line 85) | def openRow(self, row):
  function save_npy (line 95) | def save_npy(vd, p, sheet):

FILE: visidata/loaders/odf.py
  function open_ods (line 5) | def open_ods(vd, p):
  class OdsIndexSheet (line 9) | class OdsIndexSheet(IndexSheet):
    method iterload (line 10) | def iterload(self):
  function _get_cell_string_value (line 19) | def _get_cell_string_value(cell, text_s):
  class OdsSheet (line 37) | class OdsSheet(SequenceSheet):
    method iterload (line 38) | def iterload(self):

FILE: visidata/loaders/orgmode.py
  function open_org (line 34) | def open_org(vd, p):
  function open_forg (line 39) | def open_forg(vd, p):
  function open_orgdir (line 44) | def open_orgdir(vd, p):
  function encode_date (line 48) | def encode_date(dt=None):
  class OrgContentsColumn (line 58) | class OrgContentsColumn(WritableColumn):
    method setValue (line 59) | def setValue(self, row, v, setModified=True):
    method putValue (line 63) | def putValue(self, row, v):
  function sectionize (line 67) | def sectionize(lines):
  function orgmode_parse (line 90) | def orgmode_parse(all_lines):
  function _replace (line 126) | def _replace(node, newnode):
  function orgmode_parse_into (line 132) | def orgmode_parse_into(toprow, text):
  function orgmode_to_string (line 142) | def orgmode_to_string(section, prestars=''):
  function orgmode_parse_title (line 154) | def orgmode_parse_title(line):
  class OrgSheet (line 167) | class OrgSheet(Sheet):
    method __init__ (line 192) | def __init__(self, *args, **kwargs):
    method isSelectedParents (line 198) | def isSelectedParents(self, row):
    method isSelected (line 201) | def isSelected(self, row):
    method refreshRows (line 204) | def refreshRows(self):
    method _deepiter (line 207) | def _deepiter(self, objlist, depth=1):
    method openRows (line 217) | def openRows(self, rows):
    method closeRows (line 222) | def closeRows(self, rows=None):
    method newRow (line 230) | def newRow(self):
    method iterload (line 233) | def iterload(self):
    method parse_orgmd (line 263) | def parse_orgmd(self, path):
    method draw (line 274) | def draw(self, scr):
    method putChanges (line 278) | def putChanges(self):
    method save_all (line 299) | def save_all(self):
    method save (line 304) | def save(self, row):
  function paste_into (line 315) | def paste_into(sheet, row, sourcerows, cols):
  function paste_data_into (line 324) | def paste_data_into(sheet, row, sourcerows, cols):
  function combine_rows (line 332) | def combine_rows(sheet, rows):
  function _root (line 348) | def _root(row):
  function sysopen_row (line 355) | def sysopen_row(sheet, row):
  function save_org (line 363) | def save_org(vd, p, *vsheets):
  function sysopen_rows (line 374) | def sysopen_rows(sheet, rows):

FILE: visidata/loaders/pandas_freqtbl.py
  class DataFrameRowSliceAdapter (line 6) | class DataFrameRowSliceAdapter:
    method __init__ (line 13) | def __init__(self, df, mask):
    method __len__ (line 28) | def __len__(self):
    method __getitem__ (line 31) | def __getitem__(self, k):
    method __iter__ (line 39) | def __iter__(self):
    method __getattr__ (line 45) | def __getattr__(self, k):
  class DataFrameRowSliceIter (line 49) | class DataFrameRowSliceIter:
    method __init__ (line 50) | def __init__(self, df, mask_iloc, index=0):
    method __next__ (line 55) | def __next__(self):
  function makePandasFreqTable (line 65) | def makePandasFreqTable(sheet, *groupByCols):
  class PandasFreqTableSheet (line 70) | class PandasFreqTableSheet(PivotSheet):
    method selectRow (line 74) | def selectRow(self, row):
    method unselectRow (line 82) | def unselectRow(self, row):
    method addUndoSelection (line 86) | def addUndoSelection(self):
    method updateLargest (line 90) | def updateLargest(self, grouprow):
    method loader (line 93) | def loader(self):
    method openRow (line 166) | def openRow(self, row):
  function expand_source_rows (line 170) | def expand_source_rows(sheet, row):

FILE: visidata/loaders/parquet.py
  function open_parquet (line 6) | def open_parquet(vd, p):
  class ParquetColumn (line 10) | class ParquetColumn(Column):
    method readonly (line 12) | def readonly(self):
    method calcValue (line 15) | def calcValue(self, row):
    method putValue (line 27) | def putValue(self, row, val):
  class ParquetSheet (line 31) | class ParquetSheet(Sheet):
    method iterload (line 33) | def iterload(self):
  function save_parquet (line 57) | def save_parquet(vd, p, sheet):

FILE: visidata/loaders/pcap.py
  function open_pcap (line 19) | def open_pcap(vd, p):
  function manuf (line 26) | def manuf(mac):
  function macaddr (line 29) | def macaddr(addrbytes):
  function macmanuf (line 33) | def macmanuf(mac):
  function norm_host (line 49) | def norm_host(host):
  function FlagGetter (line 75) | def FlagGetter(flagfield):
  function init_pcap (line 81) | def init_pcap():
  function read_pcap (line 99) | def read_pcap(f):
  function load_oui (line 108) | def load_oui(url):
  function load_iana (line 122) | def load_iana(url):
  class Host (line 132) | class Host:
    method get_host (line 137) | def get_host(cls, pkt, field='src'):
    method get_by_ip (line 156) | def get_by_ip(cls, ip):
    method __init__ (line 163) | def __init__(self, mac, ip):
    method __str__ (line 168) | def __str__(self):
    method __lt__ (line 171) | def __lt__(self, x):
    method hostname (line 177) | def hostname(self):
  function load_consts (line 180) | def load_consts(outdict, module, attrprefix):
  function getTuple (line 186) | def getTuple(pkt):
  function getService (line 199) | def getService(tup):
  function get_transport (line 208) | def get_transport(pkt):
  function get_port (line 234) | def get_port(pkt, field='sport'):
  class EtherSheet (line 238) | class EtherSheet(Sheet):
  class IPSheet (line 251) | class IPSheet(Sheet):
    method iterload (line 269) | def iterload(self):
  class TCPSheet (line 276) | class TCPSheet(IPSheet):
    method iterload (line 284) | def iterload(self):
  class UDPSheet (line 289) | class UDPSheet(IPSheet):
    method iterload (line 297) | def iterload(self):
  class PcapSheet (line 303) | class PcapSheet(Sheet):
    method iterload (line 325) | def iterload(self):
  class PcapFlowsSheet (line 353) | class PcapFlowsSheet(Sheet):
    method iterload (line 368) | def iterload(self):
    method openRow (line 393) | def openRow(self, row):
  function flowname (line 397) | def flowname(flow):
  function try_apply (line 400) | def try_apply(func, *args, **kwargs):

FILE: visidata/loaders/pdf.py
  function open_pdf (line 9) | def open_pdf(vd, p):
  class PdfMinerSheet (line 15) | class PdfMinerSheet(TableSheet):
    method iterload (line 22) | def iterload(self):
  class TabulaSheet (line 40) | class TabulaSheet(IndexSheet):
    method iterload (line 41) | def iterload(self):

FILE: visidata/loaders/png.py
  function open_png (line 7) | def open_png(vd, p):
  class PNGSheet (line 11) | class PNGSheet(Sheet):
    method newRow (line 17) | def newRow(self):
    method iterload (line 20) | def iterload(self):
  class PNGDrawing (line 31) | class PNGDrawing(Canvas):
    method __init__ (line 35) | def __init__(self, *args, **kwargs):
    method togglePixel (line 38) | def togglePixel(self, rows):
    method setPixel (line 45) | def setPixel(self, rows, attr):
    method reload (line 53) | def reload(self):
  function save_png (line 62) | def save_png(vd, p, vs):
  function blockchar (line 81) | def blockchar(i:int):

FILE: visidata/loaders/postgres.py
  function codeToType (line 10) | def codeToType(type_code, colname):
  function openurl_rds (line 24) | def openurl_rds(vd, url, filetype=None):
  function openurl_postgres (line 45) | def openurl_postgres(vd, url, filetype=None):
  class SQL (line 63) | class SQL:
    method __init__ (line 64) | def __init__(self, conn):
    method cur (line 67) | def cur(self, qstr):
    method query_async (line 75) | def query_async(self, qstr, callback=None):
  function postgresGetColumns (line 82) | def postgresGetColumns(vd, cur):
  class PgTablesSheet (line 88) | class PgTablesSheet(Sheet):
    method loader (line 91) | def loader(self):
    method openRow (line 117) | def openRow(self, row):
  class PgTable (line 122) | class PgTable(Sheet):
    method reload (line 124) | def reload(self):

FILE: visidata/loaders/psv.py
  function open_psv (line 5) | def open_psv(vd, p):
  class PsvSheet (line 10) | class PsvSheet(TsvSheet):
  function save_psv (line 15) | def save_psv(vd, p, vs):

FILE: visidata/loaders/rec.py
  function open_rec (line 7) | def open_rec(vd, p):
  function decode_multiline (line 10) | def decode_multiline(line, fp):
  function encode_multiline (line 27) | def encode_multiline(s):
  function get_kv (line 31) | def get_kv(line):
  class RecSheet (line 34) | class RecSheet(TableSheet):
    method addColumn (line 35) | def addColumn(self, *cols, index=None):
  class RecIndexSheet (line 42) | class RecIndexSheet(IndexSheet):
    method iterload (line 43) | def iterload(self):
  function save_rec (line 125) | def save_rec(vd, p, *vsheets):

FILE: visidata/loaders/s3.py
  class S3Path (line 37) | class S3Path(Path):
    method __init__ (line 42) | def __init__(self, path, version_aware=None, version_id=None):
    method fs (line 49) | def fs(self):
    method fs (line 61) | def fs(self, val):
    method open (line 64) | def open(self, mode='r', **kwargs):
  class S3DirSheet (line 94) | class S3DirSheet(Sheet):
    method __init__ (line 109) | def __init__(self, name, source, version_aware=None):
    method object_display_name (line 123) | def object_display_name(self, row):
    method iterload (line 136) | def iterload(self):
    method download (line 163) | def download(self, rows, savepath):
    method open_rows (line 171) | def open_rows(self, rows):
    method join_rows (line 184) | def join_rows(self, rows):
    method refresh_path (line 197) | def refresh_path(self, path=None):
    method toggle_versioning (line 205) | def toggle_versioning(self):
  function openurl_s3 (line 217) | def openurl_s3(vd, p, filetype):

FILE: visidata/loaders/sas.py
  function open_xpt (line 11) | def open_xpt(vd, p):
  function open_sas7bdat (line 15) | def open_sas7bdat(vd, p):
  class XptSheet (line 18) | class XptSheet(Sheet):
    method iterload (line 19) | def iterload(self):
  class SasSheet (line 38) | class SasSheet(Sheet):
    method iterload (line 39) | def iterload(self):

FILE: visidata/loaders/scrape.py
  function soup (line 15) | def soup(vd, s):
  function open_scrape (line 22) | def open_scrape(vd, p):
  function node_name (line 33) | def node_name(node):
  function calc_selector (line 44) | def calc_selector(node):
  class HtmlAttrColumn (line 62) | class HtmlAttrColumn(Column):
    method calcValue (line 63) | def calcValue(self, row):
  function prev_header (line 67) | def prev_header(r):
  class HtmlElementsSheet (line 77) | class HtmlElementsSheet(TableSheet):
    method iterload (line 98) | def iterload(self):
    method html_parents (line 104) | def html_parents(self, row):
    method rootSource (line 110) | def rootSource(self):
    method openRows (line 113) | def openRows(self, rows):
    method openRow (line 117) | def openRow(self, row):
  class DocsSelectorColumn (line 122) | class DocsSelectorColumn(Column):
    method calcValue (line 123) | def calcValue(self, row):
  class SelectorColumn (line 126) | class SelectorColumn(Column):
    method calcValue (line 127) | def calcValue(self, row):
  class HtmlDocsSheet (line 132) | class HtmlDocsSheet(TableSheet):
    method iterload (line 149) | def iterload(self):
    method addRow (line 157) | def addRow(self, row, index=None):
    method openRow (line 161) | def openRow(self, row):
  function soupstr (line 164) | def soupstr(coll):
  function scrape_urls (line 170) | def scrape_urls(sheet, col, rows):

FILE: visidata/loaders/shp.py
  function open_shp (line 10) | def open_shp(vd, p):
  function shptype (line 24) | def shptype(ftype, declen):
  class ShapeSheet (line 31) | class ShapeSheet(Sheet):
    method iterload (line 36) | def iterload(self):
    method reloadCols (line 43) | def reloadCols(self):
  class ShapeMap (line 52) | class ShapeMap(InvertedCanvas):
    method reload (line 57) | def reload(self):
  function save_geojson (line 82) | def save_geojson(vd, p, vs):

FILE: visidata/loaders/spss.py
  function open_spss (line 5) | def open_spss(vd, p):
  class SpssSheet (line 10) | class SpssSheet(Sheet):
    method loader (line 11) | def loader(self):

FILE: visidata/loaders/sqlite.py
  function requery (line 10) | def requery(url, **kwargs):
  function guess_sqlite (line 21) | def guess_sqlite(vd, p):
  function open_sqlite (line 27) | def open_sqlite(vd, p):
  function openurl_sqlite (line 33) | def openurl_sqlite(vd, p, filetype=None):
  class SqliteSheet (line 40) | class SqliteSheet(Sheet):
    method conn (line 47) | def conn(self):
    method rawSql (line 60) | def rawSql(self, q:str) -> 'SqliteSheet':
    method sidebar (line 64) | def sidebar(self):
    method execute (line 70) | def execute(self, conn, sql, parms=None):
    method iterload_table (line 75) | def iterload_table(self, tblname:str):
    method iterload_query (line 114) | def iterload_query(self, query:str):
    method iterload (line 132) | def iterload(self):
    method putChanges (line 141) | def putChanges(self):
  class SqliteIndexSheet (line 205) | class SqliteIndexSheet(SqliteSheet, IndexSheet):
    method iterload (line 210) | def iterload(self):
    method putChanges (line 217) | def putChanges(self):
  function save_sqlite (line 243) | def save_sqlite(vd, p, *vsheets):

FILE: visidata/loaders/texttables.py
  function save_table (line 7) | def save_table(path, *sheets, fmt=fmt):

FILE: visidata/loaders/toml.py
  function open_toml (line 12) | def open_toml(vd, p):
  class TomlSheet (line 16) | class TomlSheet(PythonSheet):
    method loader (line 35) | def loader(self):

FILE: visidata/loaders/tsv.py
  function open_tsv (line 16) | def open_tsv(vd, p):
  function adaptive_bufferer (line 20) | def adaptive_bufferer(fp, max_buffer_size=65536):
  function splitter (line 52) | def splitter(stream, delim='\n'):
  class TsvSheet (line 69) | class TsvSheet(SequenceSheet):
    method iterload (line 70) | def iterload(self):
  function save_tsv (line 100) | def save_tsv(vd, p, vs):

FILE: visidata/loaders/ttf.py
  function open_ttf (line 5) | def open_ttf(vd, p):
  class TTFTablesSheet (line 10) | class TTFTablesSheet(Sheet):
    method openRow (line 23) | def openRow(self, row):
    method iterload (line 26) | def iterload(self):
  class TTFGlyphsSheet (line 34) | class TTFGlyphsSheet(Sheet):
    method openRow (line 45) | def openRow(self, row):
    method iterload (line 48) | def iterload(self):
  function makePen (line 55) | def makePen(*args, **kwargs):

FILE: visidata/loaders/unzip_http.py
  function error (line 62) | def error(s):
  function warning (line 65) | def warning(s):
  function get_bits (line 68) | def get_bits(val:int, *args):
  class RemoteZipInfo (line 76) | class RemoteZipInfo:
    method __init__ (line 77) | def __init__(self, filename:str='',
    method is_dir (line 92) | def is_dir(self):
    method parse_extra (line 95) | def parse_extra(self, extra):
  class RemoteZipFile (line 120) | class RemoteZipFile:
    method __init__ (line 128) | def __init__(self, url):
    method __enter__ (line 134) | def __enter__(self):
    method __exit__ (line 137) | def __exit__(self, a, b, c):
    method files (line 141) | def files(self):
    method infolist (line 146) | def infolist(self):
    method namelist (line 149) | def namelist(self):
    method infoiter (line 152) | def infoiter(self):
    method extract (line 220) | def extract(self, member, path=None, pwd=None):
    method extractall (line 236) | def extractall(self, path=None, members=None, pwd=None):
    method get_range (line 240) | def get_range(self, start, n):
    method matching_files (line 243) | def matching_files(self, *globs):
    method open (line 248) | def open(self, fn):
    method open_text (line 269) | def open_text(self, fn):
  class RemoteZipStream (line 273) | class RemoteZipStream(io.RawIOBase):
    method __init__ (line 274) | def __init__(self, fp, info):
    method readable (line 280) | def readable(self):
    method readinto (line 283) | def readinto(self, b):
    method read (line 288) | def read(self, n):
  class StreamProgress (line 304) | class StreamProgress:
    method __init__ (line 305) | def __init__(self, fp, name='', total=0):
    method read (line 313) | def read(self, n):
  function list_files (line 329) | def list_files(rzf):
  function extract_one (line 340) | def extract_one(outfile, rzf, f, ofname):
  function download_file (line 348) | def download_file(f, rzf, args):
  function main (line 365) | def main():

FILE: visidata/loaders/usv.py
  function open_usv (line 5) | def open_usv(vd, p):
  class UsvSheet (line 11) | class UsvSheet(TsvSheet):
  function save_usv (line 16) | def save_usv(vd, p, vs):

FILE: visidata/loaders/vcf.py
  function open_vcf (line 7) | def open_vcf(vd, p):
  function unbox (line 10) | def unbox(col, row):
  class VcfSheet (line 19) | class VcfSheet(PythonSheet):
    method reload (line 22) | def reload(self):

FILE: visidata/loaders/vds.py
  function open_vds (line 11) | def open_vds(vd, p):
  function save_vds (line 16) | def save_vds(vd, p, *sheets):
  class VdsIndexSheet (line 47) | class VdsIndexSheet(IndexSheet):
    method iterload (line 48) | def iterload(self):
  class VdsSheet (line 61) | class VdsSheet(JsonSheet):
    method newRow (line 62) | def newRow(self):
    method iterload (line 65) | def iterload(self):

FILE: visidata/loaders/vdx.py
  function open_vdx (line 11) | def open_vdx(vd, p):
  class CommandLogSimple (line 17) | class CommandLogSimple(CommandLogBase, Sheet):
    method iterload (line 19) | def iterload(self):
  function save_vdx (line 61) | def save_vdx(vd, p, *vsheets):
  function runvdx (line 83) | def runvdx(vd, vdx:str):

FILE: visidata/loaders/xlsb.py
  function guess_xls (line 6) | def guess_xls(vd, p):
  function open_xlsb (line 12) | def open_xlsb(vd, p):
  class XlsbIndex (line 16) | class XlsbIndex(IndexSheet):
    method iterload (line 17) | def iterload(self):

FILE: visidata/loaders/xlsx.py
  function open_xls (line 16) | def open_xls(vd, p):
  function open_xlsx (line 21) | def open_xlsx(vd, p):
  class XlsxIndexSheet (line 25) | class XlsxIndexSheet(IndexSheet):
    method iterload (line 37) | def iterload(self):
  class XlsxSheet (line 45) | class XlsxSheet(SequenceSheet):
    method setCols (line 50) | def setCols(self, headerrows):
    method addRow (line 71) | def addRow(self, row, index=None):
    method iterload (line 77) | def iterload(self):
    method addXlsxMetaColumns (line 84) | def addXlsxMetaColumns(self, column_letter, column_name):
    method paste_after (line 98) | def paste_after(self, rowidx):
  class XlsIndexSheet (line 103) | class XlsIndexSheet(IndexSheet):
    method iterload (line 113) | def iterload(self):
  class XlsSheet (line 120) | class XlsSheet(SequenceSheet):
    method iterload (line 121) | def iterload(self):
  function xls_name (line 128) | def xls_name(vs):
  function save_xlsx (line 139) | def save_xlsx(vd, p, *sheets):
  function save_xls (line 182) | def save_xls(vd, p, *sheets):
  function colorize_xlsx_cell (line 208) | def colorize_xlsx_cell(sheet, col, row):
  function xlsx_color_to_xterm256 (line 222) | def xlsx_color_to_xterm256(sheet, color) -> str:
  function theme_and_tint_to_rgb (line 240) | def theme_and_tint_to_rgb(sheet, theme, tint) -> str:
  function theme_colors (line 249) | def theme_colors(sheet):
  function rgb_to_ms_hls (line 271) | def rgb_to_ms_hls(red, green=None, blue=None):
  function ms_hls_to_rgb (line 285) | def ms_hls_to_rgb(hue, lightness=None, saturation=None):
  function rgb_to_hex (line 291) | def rgb_to_hex(red, green=None, blue=None):
  function tint_luminance (line 297) | def tint_luminance(tint, lum):

FILE: visidata/loaders/xml.py
  function open_xml (line 8) | def open_xml(vd, p):
  function unns (line 13) | def unns(k):
  function AttribColumn (line 20) | def AttribColumn(name, k, **kwargs):
  class XmlSheet (line 26) | class XmlSheet(Sheet):
    method showColumnsBasedOnRow (line 43) | def showColumnsBasedOnRow(self, row):
    method iterload (line 49) | def iterload(self):
    method openRow (line 72) | def openRow(self, row):
    method addRow (line 75) | def addRow(self, elem):
  function save_xml (line 86) | def save_xml(vd, p, vs):

FILE: visidata/loaders/xword.py
  function open_puz (line 10) | def open_puz(vd, p):
  function open_xd (line 14) | def open_xd(vd, p):
  class CrosswordsSheet (line 21) | class CrosswordsSheet(Sheet):
    method reload (line 33) | def reload(self):
    method openRow (line 38) | def openRow(self):
  class GridSheet (line 42) | class GridSheet(Sheet):
    method reload (line 49) | def reload(self):
  class CrosswordSheet (line 75) | class CrosswordSheet(Sheet):
    method reload (line 85) | def reload(self):
    method openRow (line 90) | def openRow(self):
  class PuzSheet (line 94) | class PuzSheet(CrosswordSheet):
    method reload (line 96) | def reload(self):
  function save_xd (line 103) | def save_xd(vd, p, vs):

FILE: visidata/loaders/yaml.py
  function open_yml (line 7) | def open_yml(vd, p):
  class YamlSheet (line 12) | class YamlSheet(JsonSheet):
    method iterload (line 13) | def iterload(self):

FILE: visidata/macros.py
  class MacroSheet (line 15) | class MacroSheet(IndexSheet):
    method iterload (line 33) | def iterload(self):
    method commitDeleteRow (line 36) | def commitDeleteRow(self, row):
    method putChanges (line 52) | def putChanges(self):
    method newRow (line 60) | def newRow(self):
  function macrosheet (line 65) | def macrosheet(vd):
  function loadMacro (line 70) | def loadMacro(vd, p:Path):
  function runMacro (line 85) | def runMacro(vd, binding:str):
  function setMacro (line 93) | def setMacro(vd, ks:str, vs, helpstr=''):
  function saveMacro (line 105) | def saveMacro(self, rows, ks):
  function afterExecSheet (line 119) | def afterExecSheet(cmdlog, sheet, escaped, err):
  function startMacro (line 131) | def startMacro(cmdlog):
  function run (line 158) | def run(vd, *args, **kwargs):
  function reloadMacros (line 163) | def reloadMacros(vd):

FILE: visidata/main.py
  function eval_vd (line 37) | def eval_vd(logpath, *args, **kwargs):
  function duptty (line 61) | def duptty():
  function parsePos (line 97) | def parsePos(vd, arg:str, inputs:'list[tuple[str, dict]]'=None):
  function outputProgressEvery (line 139) | def outputProgressEvery(vd, sheet, seconds:float=0.5):
  function moveToPos (line 155) | def moveToPos(vd, sources, sheet_desc, startcol, startrow):
  function sheet_from_description (line 177) | def sheet_from_description(vd, sources, sheet_desc):
  function queue_move_to_pos (line 233) | def queue_move_to_pos(vd, sources, moves):
  function attempt_move_to_pos (line 248) | def attempt_move_to_pos(vd, sources, sheet_desc, startcol, startrow):
  function afterLoad (line 275) | def afterLoad(sheet):
  function main_vd (line 283) | def main_vd():
  function vd_cli (line 533) | def vd_cli():

FILE: visidata/mainloop.py
  class ReturnValue (line 20) | class ReturnValue(BaseException):
  function callNoExceptions (line 26) | def callNoExceptions(vd, func, *args, **kwargs):
  function callIgnoreExceptions (line 35) | def callIgnoreExceptions(vd, func, *args, **kwargs):
  function drawSheet (line 46) | def drawSheet(vd, scr, sheet):
  function setWindows (line 75) | def setWindows(vd, scr, pct=None):
  function draw_all (line 125) | def draw_all(vd):
  function runresult (line 164) | def runresult(vd):
  function mainloop (line 174) | def mainloop(vd, scr):
  function _playNextQueuedCommand (line 264) | def _playNextQueuedCommand(vd):
  function get_curses_timeout (line 282) | def get_curses_timeout(vd) -> int:
  function initCurses (line 310) | def initCurses(vd):
  function wrapper (line 339) | def wrapper(f, *args, **kwargs):
  function run (line 348) | def run(vd, *sheetlist):
  function addCommand (line 375) | def addCommand(vd, *args, **kwargs):

FILE: visidata/memory.py
  function memoValue (line 8) | def memoValue(vd, name, value, dispvalue):
  class MemorySheet (line 13) | class MemorySheet(Sheet):
    method rows (line 21) | def rows(self):
    method rows (line 25) | def rows(self, v):
  function memosSheet (line 30) | def memosSheet(vd):
  function inputMemoName (line 35) | def inputMemoName(vd, value):

FILE: visidata/menu.py
  function hintStatus (line 27) | def hintStatus(vd):
  function menudraw (line 36) | def menudraw(*args):
  function Menu (line 40) | def Menu(title, *args):
  function walkmenu (line 48) | def walkmenu(item, menupath=[]):
  function _finditem (line 56) | def _finditem(menus, item:Union[str,int]):
  function getMenuItem (line 67) | def getMenuItem(sheet, menupath:List[str]=None):
  function addMenuItem (line 83) | def addMenuItem(vd, *args):
  function addMenuItems (line 92) | def addMenuItems(vd, *itemgroups):
  function addMenu (line 107) | def addMenu(vd, *args):
  function _intMenuPath (line 128) | def _intMenuPath(obj, menupath):
  function menuitemAvailable (line 159) | def menuitemAvailable(sheet, item):
  function drawSubmenu (line 164) | def drawSubmenu(vd, scr, sheet, y, x, menus, level, disp_menu_boxchars=''):
  function nop (line 249) | def nop(vd, *args, **kwargs):
  function _done (line 253) | def _done(vd, *args, **kwargs):
  function menus (line 260) | def menus(sheet):
  function drawMenu (line 295) | def drawMenu(vd, scr, sheet):
  function pressMenu (line 386) | def pressMenu(sheet, *args):
  function checkMenu (line 407) | def checkMenu(sheet):
  function runMenu (line 412) | def runMenu(vd):

FILE: visidata/metasheets.py
  class ColumnsSheet (line 14) | class ColumnsSheet(Sheet):
    class ValueColumn (line 37) | class ValueColumn(WritableColumn):
      method calcValue (line 39) | def calcValue(self, srcCol):
      method setValue (line 41) | def setValue(self, srcCol, val, setModified=True):
    method nSourceCols (line 70) | def nSourceCols(self):
    method loader (line 73) | def loader(self):
    method newRow (line 81) | def newRow(self):
  class MetaSheet (line 86) | class MetaSheet(Sheet):
  class VisiDataMetaSheet (line 89) | class VisiDataMetaSheet(TsvSheet):
  function allColumnsSheet (line 101) | def allColumnsSheet(vd):
  function save_visidatarc (line 106) | def save_visidatarc(vd, p, vs):
  function join_cols (line 118) | def join_cols(sheet):

FILE: visidata/modify.py
  function couldOverwrite (line 18) | def couldOverwrite(vd) -> bool:
  function confirmOverwrite (line 24) | def confirmOverwrite(vd, path, msg:str=''):
  function _deferredAdds (line 39) | def _deferredAdds(sheet):
  function _deferredMods (line 43) | def _deferredMods(sheet):
  function _deferredDels (line 47) | def _deferredDels(sheet):
  function preloadHook (line 58) | def preloadHook(sheet):
  function afterLoad (line 65) | def afterLoad(sheet):
  function rowAdded (line 69) | def rowAdded(self, row):
  function cellChanged (line 80) | def cellChanged(col, row, val):
  function rowDeleted (line 116) | def rowDeleted(self, row):
  function addRows (line 131) | def addRows(sheet, rows, index=None, undo=True):
  function deleteBy (line 152) | def deleteBy(sheet, func, commit=False, undo=True):
  function isDeleted (line 200) | def isDeleted(self, row):
  function isChanged (line 205) | def isChanged(self, col, row):
  function getSourceValue (line 218) | def getSourceValue(col, row):
  function commitAdds (line 224) | def commitAdds(self):
  function commitMods (line 244) | def commitMods(sheet):
  function commitDeletes (line 261) | def commitDeletes(self):
  function commitAddRow (line 271) | def commitAddRow(self, row):
  function commitDeleteRow (line 276) | def commitDeleteRow(self, row):
  function putChanges (line 282) | def putChanges(sheet):
  function getDeferredChanges (line 292) | def getDeferredChanges(sheet):
  function changestr (line 312) | def changestr(self, adds, mods, deletes):
  function commit (line 329) | def commit(sheet, *rows):
  function new_rows (line 344) | def new_rows(sheet, n):

FILE: visidata/motd.py
  function domotd (line 22) | def domotd(vd):

FILE: visidata/mouse.py
  function initCurses (line 15) | def initCurses(vd):
  function enableMouse (line 31) | def enableMouse(vd, b:bool) -> bool:  #2913 #2851
  function clearCaches (line 40) | def clearCaches(vd):
  function onMouse (line 45) | def onMouse(vd, scr, x, y, w, h, **kwargs):
  function getMouse (line 52) | def getMouse(vd, _x, _y, button):
  function parseMouse (line 59) | def parseMouse(vd, **kwargs):
  function handleMouse (line 90) | def handleMouse(vd, sheet):
  function visibleColAtX (line 127) | def visibleColAtX(sheet, x):
  function visibleRowAtY (line 134) | def visibleRowAtY(sheet, y):
  function go_mouse (line 141) | def go_mouse(sheet):

FILE: visidata/movement.py
  function rotateRange (line 6) | def rotateRange(n, idx, reverse=False):
  function pageLeft (line 26) | def pageLeft(self):
  function moveToNextRow (line 64) | def moveToNextRow(vs, func, reverse=False, msg='no different value up th...
  function visibleWidth (line 84) | def visibleWidth(self):

FILE: visidata/optionssheet.py
  function optionsSheet (line 8) | def optionsSheet(sheet):
  function globalOptionsSheet (line 12) | def globalOptionsSheet(vd):
  class OptionsSheet (line 17) | class OptionsSheet(Sheet):
    method guide (line 38) | def guide(self):
    method diffOption (line 49) | def diffOption(self, optname):
    method editOption (line 52) | def editOption(self, row):
    method valueColName (line 70) | def valueColName(self):
    method beforeLoad (line 73) | def beforeLoad(self):
    method iterload (line 77) | def iterload(self):
    method newRow (line 87) | def newRow(self):
  function commit (line 96) | def commit(sheet, *rows):

FILE: visidata/path.py
  function pkg_resources_files (line 40) | def pkg_resources_files(vd, package):
  function vstat (line 52) | def vstat(path, force=False):
  function filesize (line 58) | def filesize(path):
  function modtime (line 68) | def modtime(path):
  class BytesIOWrapper (line 74) | class BytesIOWrapper(io.BufferedReader):
    method __init__ (line 77) | def __init__(self, text_io_buffer, encoding=None, errors=None, **kwargs):
    method _encoding_call (line 82) | def _encoding_call(self, method_name, *args, **kwargs):
    method read (line 87) | def read(self, size=-1):
    method read1 (line 90) | def read1(self, size=-1):
    method peek (line 93) | def peek(self, size=-1):
  class FileProgress (line 97) | class FileProgress:
    method __init__ (line 99) | def __init__(self, path, fp, mode='r', **kwargs):
    method close (line 123) | def close(self, *args, **kwargs):
    method read (line 129) | def read(self, size=-1):
    method readline (line 136) | def readline(self, size=-1):
    method __getattr__ (line 142) | def __getattr__(self, k):
    method __enter__ (line 145) | def __enter__(self):
    method __next__ (line 149) | def __next__(self):
    method __iter__ (line 154) | def __iter__(self):
    method __exit__ (line 162) | def __exit__(self, type, value, tb):
  class Path (line 166) | class Path(os.PathLike):
    method __init__ (line 168) | def __init__(self, given, fp=None, fptext=None, lines=None, filesize=N...
    method name (line 178) | def name(self):
    method given (line 185) | def given(self):
    method given (line 190) | def given(self, given):
    method options (line 215) | def options(self):
    method __getattr__ (line 218) | def __getattr__(self, k):
    method __fspath__ (line 230) | def __fspath__(self):
    method __lt__ (line 233) | def __lt__(self, a):
    method __truediv__ (line 238) | def __truediv__(self, a):
    method has_fp (line 241) | def has_fp(self):
    method open (line 245) | def open(self, mode='rt', encoding=None, encoding_errors=None, newline...
    method open_bytes (line 251) | def open_bytes(self, mode='rb'):
    method open_text (line 271) | def open_text(self, mode='rt', encoding=None, encoding_errors=None, ne...
    method read_text (line 303) | def read_text(self, *args, **kwargs):
    method _open (line 320) | def _open(self, *args, **kwargs):
    method __iter__ (line 351) | def __iter__(self):
    method read_bytes (line 356) | def read_bytes(self):
    method is_fifo (line 362) | def is_fifo(self):
    method is_local (line 366) | def is_local(self):
    method is_url (line 370) | def is_url(self):
    method __str__ (line 374) | def __str__(self):
    method stat (line 381) | def stat(self, force=False):
    method exists (line 390) | def exists(self):
    method scheme (line 397) | def scheme(self):
    method iterdir (line 402) | def iterdir(self):  #2188
    method with_name (line 406) | def with_name(self, name):
  class RepeatFile (line 416) | class RepeatFile:
    method __init__ (line 419) | def __init__(self, iter_lines, lines=None):
    method __enter__ (line 426) | def __enter__(self):
    method __exit__ (line 430) | def __exit__(self, a,b,c):
    method reopen (line 433) | def reopen(self):
    method read (line 437) | def read(self, n=None):
    method write (line 457) | def write(self, s):
    method tell (line 460) | def tell(self):
    method seek (line 464) | def seek(self, offset, whence=io.SEEK_SET):
    method readline (line 475) | def readline(self, size=-1):
    method __iter__ (line 483) | def __iter__(self):
    method __next__ (line 486) | def __next__(self):
    method readable (line 489) | def readable(self):
    method writable (line 492) | def writable(self):
    method seekable (line 495) | def seekable(self):
    method read1 (line 498) | def read1(self, n=-1):
    method peek (line 501) | def peek(self, n=-1):
    method exists (line 507) | def exists(self):
  class RepeatFileIter (line 511) | class RepeatFileIter:
    method __init__ (line 512) | def __init__(self, rf):
    method __iter__ (line 516) | def __iter__(self):
    method __next__ (line 519) | def __next__(self):

FILE: visidata/pivot.py
  function makePivot (line 14) | def makePivot(source, groupByCols, pivotCols):
  function makeErrorKey (line 20) | def makeErrorKey(col):
  function formatRange (line 26) | def formatRange(col, numeric_key):
  class RangeColumn (line 38) | class RangeColumn(Column):
    method __init__ (line 39) | def __init__(self, *args, **kwargs):
    method formatter_range (line 43) | def formatter_range(self, fmtstr):
    method _format (line 46) | def _format(self, typedval, *args, **kwargs):
  class AggrColumn (line 52) | class AggrColumn(Column):
    method calcValue (line 53) | def calcValue(col, row):
  function makeAggrColumn (line 59) | def makeAggrColumn(aggcol, aggregator):
  class PivotSheet (line 71) | class PivotSheet(Sheet):
    method __init__ (line 75) | def __init__(self, *names, groupByCols=[], pivotCols=[], **kwargs):
    method isNumericRange (line 81) | def isNumericRange(self, col):
    method resetCols (line 84) | def resetCols(self):
    method openRow (line 105) | def openRow(self, row):
    method openCell (line 112) | def openCell(self, col, row):
    method loader (line 119) | def loader(self):
    method addAggregateCols (line 125) | def addAggregateCols(self):
    method groupRows (line 190) | def groupRows(self, rowfunc=None):
    method afterLoad (line 284) | def afterLoad(self):
  function addcol_aggr (line 294) | def addcol_aggr(sheet, col):

FILE: visidata/plugins.py
  function pluginConfig (line 17) | def pluginConfig(self):
  function pluginConfigLines (line 22) | def pluginConfigLines(self):
  function _plugin_import_name (line 25) | def _plugin_import_name(self, plugin):
  function enablePlugin (line 34) | def enablePlugin(vd, plugin:str):
  function removePlugin (line 41) | def removePlugin(vd, plugin:str):
  function pluginsSheet (line 68) | def pluginsSheet(p):
  class PluginsSheet (line 72) | class PluginsSheet(Sheet):
    method iterload (line 83) | def iterload(self):

FILE: visidata/pyobj.py
  class PythonSheet (line 16) | class PythonSheet(Sheet):
    method openRow (line 17) | def openRow(self, row):
  class PythonAtomSheet (line 20) | class PythonAtomSheet(PythonSheet):
    method loader (line 28) | def loader(self):
    method openRow (line 32) | def openRow(self, row):
    method openCell (line 34) | def openCell(self, col, row, rowidx=None):
    method openRowPyobj (line 36) | def openRowPyobj(self, rowidx):
    method openCellPyobj (line 38) | def openCellPyobj(self, col, rowidx):
    method newRow (line 40) | def newRow(self):
  function view (line 45) | def view(vd, obj):
  function PyobjColumns (line 49) | def PyobjColumns(obj):
  function AttrColumns (line 63) | def AttrColumns(attrnames):
  function SheetList (line 68) | def SheetList(*names, **kwargs):
  class ListOfPyobjSheet (line 85) | class ListOfPyobjSheet(PythonSheet):
    method loader (line 87) | def loader(self):
  class ListOfDictSheet (line 101) | class ListOfDictSheet(PythonSheet):
    method reload (line 103) | def reload(self):
  class ListOfNamedTupleSheet (line 114) | class ListOfNamedTupleSheet(PythonSheet):
    method reload (line 116) | def reload(self):
  class SheetNamedTuple (line 124) | class SheetNamedTuple(PythonSheet):
    method __init__ (line 129) | def __init__(self, *names, **kwargs):
    method reload (line 132) | def reload(self):
    method openRow (line 135) | def openRow(self, row):
  class SheetDict (line 140) | class SheetDict(PythonSheet):
    method reload (line 148) | def reload(self):
    method openRow (line 151) | def openRow(self, row):
  class ColumnSourceAttr (line 155) | class ColumnSourceAttr(WritableColumn):
    method calcValue (line 157) | def calcValue(self, attrname):
    method setValue (line 159) | def setValue(self, attrname, value, setModified=True):
  function docstring (line 165) | def docstring(obj, attr):
  class PyobjSheet (line 172) | class PyobjSheet(PythonSheet):
    method __new__ (line 183) | def __new__(cls, *names, **kwargs):
    method reload (line 209) | def reload(self):
    method openRow (line 226) | def openRow(self, row):
  function openRow (line 233) | def openRow(sheet, row, rowidx=None):
  function openCell (line 252) | def openCell(sheet, col, row, rowidx=None):
  function openRows (line 264) | def openRows(sheet, rows):
  function openCells (line 272) | def openCells(sheet, col, rows):
  function openRowPyobj (line 279) | def openRowPyobj(sheet, rowidx):
  function openCellPyobj (line 285) | def openCellPyobj(sheet, col, rowidx):
  function inputPythonExpr (line 293) | def inputPythonExpr(sheet):

FILE: visidata/rename_col.py
  function hint_rename_col (line 4) | def hint_rename_col(sheet):
  function addUndoColNames (line 10) | def addUndoColNames(vd, cols):
  function updateColNames (line 19) | def updateColNames(sheet, rows, cols, overwrite=False):

FILE: visidata/save.py
  function safe_trdict (line 12) | def safe_trdict(vs, delimiter=None):
  function iterdispvals (line 29) | def iterdispvals(sheet, *cols, format=False, delimiter=None):
  function itervals (line 78) | def itervals(sheet, *cols, format=False):
  function getDefaultSaveName (line 83) | def getDefaultSaveName(sheet):
  function saveCols (line 99) | def saveCols(vd, cols):
  function saveSheets (line 113) | def saveSheets(vd, givenpath, *vsheets, confirm_overwrite=True):
  function save_zip (line 178) | def save_zip(vd, p, *vsheets):
  function save_txt (line 194) | def save_txt(vd, p, *vsheets):
  function rootSheet (line 208) | def rootSheet(sheet):

FILE: visidata/search.py
  function moveRegex (line 17) | def moveRegex(vd, sheet, *args, **kwargs):
  function searchRegex (line 24) | def searchRegex(vd, sheet, moveCursor=False, reverse=False, regex_flags=...
  function searchInputRegex (line 80) | def searchInputRegex(sheet, action:str, columns:str='cursorCol'):
  function moveInputRegex (line 87) | def moveInputRegex(sheet, action:str, type="regex", **kwargs):
  function search_expr (line 95) | def search_expr(sheet, expr, reverse=False, curcol=None):
  function moveExpr (line 111) | def moveExpr(vd, sheet, reverse=False):
  function searchNext (line 128) | def searchNext(vd, sheet, reverse=False):
  function clear_search (line 139) | def clear_search(sheet):

FILE: visidata/selection.py
  function isSelected (line 17) | def isSelected(self, row):
  function toggle (line 23) | def toggle(self, rows, add_undo=True):
  function beforeLoad (line 35) | def beforeLoad(self):
  function select_row (line 40) | def select_row(self, row, add_undo=True):
  function toggle_row (line 48) | def toggle_row(self, row, add_undo=True):
  function unselect_row (line 59) | def unselect_row(self, row, add_undo=True):
  function selectRow (line 67) | def selectRow(self, row):
  function unselectRow (line 73) | def unselectRow(self, row):
  function clearSelected (line 82) | def clearSelected(self):
  function select (line 89) | def select(self, rows, status=True, progress=True, add_undo=True):
  function unselect (line 107) | def unselect(self, rows, status=True, progress=True, add_undo=True):
  function selectByIdx (line 118) | def selectByIdx(self, rowIdxs):
  function unselectByIdx (line 123) | def unselectByIdx(self, rowIdxs):
  function gatherBy (line 128) | def gatherBy(self, func, gerund='gathering'):
  function selectedRows (line 139) | def selectedRows(self):
  function onlySelectedRows (line 146) | def onlySelectedRows(self):
  function someSelectedRows (line 153) | def someSelectedRows(self):
  function nSelectedRows (line 165) | def nSelectedRows(self):
  function deleteSelected (line 171) | def deleteSelected(self):
  function addUndoSelection (line 181) | def addUndoSelection(sheet):
  function selectToNextRow (line 187) | def selectToNextRow(vs, func, reverse=False, msg='no selected row'):

FILE: visidata/settings.py
  class SettingsMgr (line 15) | class SettingsMgr(collections.OrderedDict):
    method __init__ (line 16) | def __init__(self):
    method __hash__ (line 20) | def __hash__(self):
    method __eq__ (line 23) | def __eq__(self, other):
    method objname (line 26) | def objname(self, obj):
    method getobj (line 46) | def getobj(self, objname):
    method unset (line 50) | def unset(self, k, obj='default'):
    method set (line 56) | def set(self, k, v, obj):
    method setdefault (line 63) | def setdefault(self, k, v):
    method _mappings (line 67) | def _mappings(self, obj):
    method _get (line 87) | def _get(self, key, obj=None):
    method iter (line 95) | def iter(self, obj=None):
    method iterall (line 107) | def iterall(self):
    method resetToDefaults (line 112) | def resetToDefaults(self):
  class Command (line 123) | class Command:
    method __init__ (line 124) | def __init__(self, longname, execstr, helpstr='', module='', replay=Tr...
  class Option (line 134) | class Option:
    method __init__ (line 135) | def __init__(self, name, value, description='', module='', help=''):
    method __str__ (line 146) | def __str__(self):
    method __eq__ (line 149) | def __eq__(self, other):
  class OptionsObject (line 154) | class OptionsObject:
    method __init__ (line 156) | def __init__(self, mgr, obj=None):
    method keys (line 161) | def keys(self, obj=None):
    method _get (line 166) | def _get(self, k, obj=None):
    method _set (line 174) | def _set(self, k, v, obj=None, helpstr='', module=None):
    method is_set (line 181) | def is_set(self, k, obj=None):
    method get (line 186) | def get(self, optname, default=None):
    method getobj (line 193) | def getobj(self, optname, obj=None):
    method getdefault (line 197) | def getdefault(self, optname):
    method getonly (line 200) | def getonly(self, optname, obj, default):
    method set (line 209) | def set(self, optname, value, obj='global', cmdlog=True):
    method unset (line 238) | def unset(self, optname, obj=None):
    method add_option_to_cmdlogs (line 247) | def add_option_to_cmdlogs(self, obj, optname, value='', longname='set-...
    method resetToDefaults (line 266) | def resetToDefaults(self):
    method setdefault (line 271) | def setdefault(self, optname, value, helpstr, module):
    method getall (line 274) | def getall(self, prefix=''):
    method __getattr__ (line 280) | def __getattr__(self, optname):      # options.foo
    method __setattr__ (line 284) | def __setattr__(self, optname, value):   # options.foo = value
    method __getitem__ (line 288) | def __getitem__(self, optname):      # options[optname]
    method __setitem__ (line 294) | def __setitem__(self, optname, value):   # options[optname] = value
  function optalias (line 307) | def optalias(vd, altname, optname, val=None):
  function _resolve_optalias (line 313) | def _resolve_optalias(vd, optname, optval):
  function option (line 323) | def option(vd, name, default, description, replay=False, sheettype=BaseS...
  function theme_option (line 342) | def theme_option(vd, name, *args, **kwargs):
  function addCommand (line 350) | def addCommand(cls, keystrokes, longname, execstr, helpstr='', replay=Tr...
  function removeCommand (line 366) | def removeCommand(cls, keystrokes, longname):
  function _command (line 376) | def _command(cls, binding, longname, helpstr, **kwargs):
  function bindkey (line 388) | def bindkey(vd, keystrokes, longname, obj='BaseSheet'):
  function unbindkey (line 393) | def unbindkey(vd, keystrokes, obj='BaseSheet'):
  function bindkey (line 400) | def bindkey(cls, keystrokes, longname):
  function unbindkey (line 409) | def unbindkey(cls, keystrokes):
  function getCommand (line 415) | def getCommand(sheet, cmd):
  function loadConfigFile (line 432) | def loadConfigFile(vd, fn=''):
  function addOptions (line 453) | def addOptions(parser):
  function config_file (line 465) | def config_file(vd):
  function cache_dir (line 474) | def cache_dir(vd):
  function data_dir (line 479) | def data_dir(vd):
  function loadConfigAndPlugins (line 484) | def loadConfigAndPlugins(vd, args=AttrDict()):
  function importModule (line 537) | def importModule(vd, pkgname, symbols=[]):
  function importSubmodules (line 552) | def importSubmodules(vd, pkgname):
  function importExternal (line 562) | def importExternal(vd, modname, pipmodname=''):
  function requireOptions (line 573) | def requireOptions(vd, *args, help=''):
  function setPersistentOptions (line 589) | def setPersistentOptions(vd, **kwargs):

FILE: visidata/sheets.py
  function _splitcell (line 32) | def _splitcell(sheet, s, width=0, maxheight=1):
  class Colorizer (line 52) | class Colorizer:
    method __init__ (line 58) | def __init__(self, precedence:int, coloropt:str, func=lambda s,c,r,v: ...
  class RowColorizer (line 63) | class RowColorizer(Colorizer):
    method func (line 64) | def func(self, s, c, r, v):
  class ColumnColorizer (line 67) | class ColumnColorizer(Colorizer):
    method func (line 68) | def func(self, s, c, r, v):
  class CellColorizer (line 71) | class CellColorizer(Colorizer):
    method func (line 72) | def func(self, s, c, r, v):
  class RecursiveExprException (line 76) | class RecursiveExprException(Exception):
  class LazyComputeRow (line 79) | class LazyComputeRow:
    method __init__ (line 81) | def __init__(self, sheet, row, col=None, **kwargs):
    method _lcm (line 91) | def _lcm(self):
    method __iter__ (line 97) | def __iter__(self):
    method keys (line 104) | def keys(self):
    method __str__ (line 107) | def __str__(self):
    method as_dict (line 110) | def as_dict(self):
    method __getattr__ (line 113) | def __getattr__(self, k):
    method __getitem__ (line 116) | def __getitem__(self, colid):
  class BasicRow (line 146) | class BasicRow(collections.defaultdict):
    method __init__ (line 147) | def __init__(self, *args):
    method __bool__ (line 149) | def __bool__(self):
  class TableSheet (line 152) | class TableSheet(BaseSheet):
    method help_title (line 161) | def help_title(self):
    method __init__ (line 180) | def __init__(self, *names, rows=UNLOADED, **kwargs):
    method topRowIndex (line 205) | def topRowIndex(self):
    method topRowIndex (line 209) | def topRowIndex(self, v):
    method addColorizer (line 213) | def addColorizer(self, c):
    method removeColorizer (line 218) | def removeColorizer(self, c):
    method classColorizers (line 223) | def classColorizers(self) -> list:
    method _colorize (line 235) | def _colorize(self, col, row, value=None) -> ColorAttr:
    method addRow (line 249) | def addRow(self, row, index=None):
    method newRow (line 259) | def newRow(self):
    method colsByName (line 264) | def colsByName(self):
    method column (line 269) | def column(self, colname):
    method recalc (line 273) | def recalc(self):
    method reload (line 279) | def reload(self):
    method beforeLoad (line 292) | def beforeLoad(self):
    method resetCols (line 295) | def resetCols(self):
    method loader (line 305) | def loader(self):
    method _iterloader (line 313) | def _iterloader(self):
    method iterload (line 323) | def iterload(self):
    method loadStart (line 328) | def loadStart(self):
    method loadSome (line 331) | def loadSome(self):
    method afterLoad (line 341) | def afterLoad(self):
    method iterrows (line 347) | def iterrows(self, gerund='iterating'):
    method __iter__ (line 361) | def __iter__(self):
    method __copy__ (line 366) | def __copy__(self):
    method bottomRowIndex (line 392) | def bottomRowIndex(self):
    method bottomRowIndex (line 396) | def bottomRowIndex(self, newidx):
    method rowHeight (line 400) | def rowHeight(self):
    method __deepcopy__ (line 404) | def __deepcopy__(self, memo):
    method __str__ (line 410) | def __str__(self):
    method __repr__ (line 413) | def __repr__(self):
    method currow (line 417) | def currow(self):
    method evalExpr (line 420) | def evalExpr(self, expr:str, row=None, col=None, **kwargs):
    method rowid (line 430) | def rowid(self, row):
    method nScreenRows (line 435) | def nScreenRows(self):
    method nHeaderRows (line 443) | def nHeaderRows(self):
    method nFooterRows (line 448) | def nFooterRows(self):
    method cursorCol (line 453) | def cursorCol(self):
    method cursorRow (line 459) | def cursorRow(self):
    method visibleRows (line 465) | def visibleRows(self):  # onscreen rows
    method visibleCols (line 470) | def visibleCols(self):  # non-hidden cols
    method keyCols (line 475) | def keyCols(self):
    method availCols (line 480) | def availCols(self):
    method availColnames (line 485) | def availColnames(self):
    method cursorColIndex (line 490) | def cursorColIndex(self):
    method nonKeyVisibleCols (line 498) | def nonKeyVisibleCols(self):
    method numericCols (line 503) | def numericCols(self):
    method keyColNames (line 508) | def keyColNames(self):
    method keyColNames (line 513) | def keyColNames(self, v):  #2122
    method cursorCell (line 520) | def cursorCell(self):
    method cursorDisplay (line 525) | def cursorDisplay(self):
    method cursorFullDisplay (line 530) | def cursorFullDisplay(self):
    method cursorTypedValue (line 535) | def cursorTypedValue(self):
    method cursorValue (line 540) | def cursorValue(self):
    method getTypedRow (line 544) | def getTypedRow(self, rownum):
    method statusLine (line 548) | def statusLine(self):
    method nRows (line 555) | def nRows(self):
    method nCols (line 560) | def nCols(self):
    method nVisibleCols (line 565) | def nVisibleCols(self):
    method cursorDown (line 569) | def cursorDown(self, n=1):
    method cursorRight (line 573) | def cursorRight(self, n=1):
    method addColumn (line 577) | def addColumn(self, *cols, index=None):
    method addColumnAtCursor (line 609) | def addColumnAtCursor(self, *cols):
    method setColNames (line 624) | def setColNames(self, rows):
    method setKeys (line 628) | def setKeys(self, cols):
    method unsetKeys (line 639) | def unsetKeys(self, cols):
    method rowkey (line 645) | def rowkey(self, row):
    method rowname (line 649) | def rowname(self, row):
    method checkCursor (line 653) | def checkCursor(self):
    method adjustColLayout (line 682) | def adjustColLayout(self):
    method calcColLayout (line 719) | def calcColLayout(self):
    method calcSingleColLayout (line 739) | def calcSingleColLayout(self, col:Column, vcolidx:int, x:int=0, minCol...
    method drawColHeader (line 768) | def drawColHeader(self, scr, y, h, vcolidx):
    method isVisibleIdxKey (line 831) | def isVisibleIdxKey(self, vcolidx):
    method allAggregators (line 836) | def allAggregators(self):
    method draw (line 846) | def draw(self, scr):
    method calc_height (line 931) | def calc_height(self, row, displines=None, isNull=None, maxheight=1):
    method drawRow (line 963) | def drawRow(self, scr, row, rowidx, ybase, rowcattr: ColorAttr, maxhei...
    method incremented_colname (line 1080) | def incremented_colname(self):
  class SequenceSheet (line 1092) | class SequenceSheet(Sheet):
    method setCols (line 1094) | def setCols(self, headerrows):
    method newRow (line 1103) | def newRow(self):
    method addRow (line 1106) | def addRow(self, row, index=None):
    method optlines (line 1114) | def optlines(self, it, optname):
    method loader (line 1122) | def loader(self):
  function _evalcontexts (line 1144) | def _evalcontexts(vd):
  function replace (line 1150) | def replace(vd, vs):
  function remove (line 1157) | def remove(vd, vs):
  function push (line 1169) | def push(vd, vs, pane=0, load=True):
  function quit (line 1199) | def quit(vd, *sheets):
  function confirmQuit (line 1220) | def confirmQuit(vs, verb='quit'):
  function preloadHook (line 1230) | def preloadHook(sheet):
  function newSheet (line 1238) | def newSheet(vd, name, ncols, **kwargs):
  function quitAndReleaseMemory (line 1245) | def quitAndReleaseMemory(vs):
  function splitPane (line 1259) | def splitPane(sheet, pct=None):
  function async_deepcopy (line 1270) | def async_deepcopy(sheet, rowlist):
  function reload_or_replace (line 1281) | def reload_or_replace(sheet):
  function formatter_enum (line 1348) | def formatter_enum(col, fmtdict):

FILE: visidata/shell.py
  function popen (line 25) | def popen(vd, *args, **kwargs):
  function killLeftoverProcesses (line 32) | def killLeftoverProcesses(vd):
  function guess_dir (line 39) | def guess_dir(vd, p):
  function currentDirSheet (line 45) | def currentDirSheet(p):
  function exec_shell (line 50) | def exec_shell(*args):
  function open_dir (line 59) | def open_dir(vd, p):
  function open_fdir (line 69) | def open_fdir(vd, p):
  function addShellColumns (line 73) | def addShellColumns(vd, cmd, sheet, curcol=None):
  class ColumnShell (line 81) | class ColumnShell(Column):
    method __init__ (line 82) | def __init__(self, name, cmd=None, curcol=None, **kwargs):
    method calcValue (line 88) | def calcValue(self, row):
  class DirSheet (line 105) | class DirSheet(Sheet):
    method colorOwner (line 157) | def colorOwner(sheet, col, row, val):
    method moveFile (line 170) | def moveFile(self, row, newparent):
    method renameFile (line 184) | def renameFile(self, row, val):
    method removeFile (line 190) | def removeFile(self, path):
    method commitDeleteRow (line 199) | def commitDeleteRow(self, r):
    method newRow (line 202) | def newRow(self):
    method iterload (line 205) | def iterload(self):
    method preloadHook (line 237) | def preloadHook(self):
    method restat (line 241) | def restat(self):
    method putChanges (line 245) | def putChanges(self):
    method getDefaultSaveName (line 253) | def getDefaultSaveName(sheet):
  class FileListSheet (line 257) | class FileListSheet(DirSheet):
    method iterload (line 259) | def iterload(self):
  function inputShell (line 265) | def inputShell(vd):
  function _openPreview (line 295) | def _openPreview(sheet, p):
  function previewFile (line 303) | def previewFile(sheet, p):
  function checkCursor (line 318) | def checkCursor(sheet):
  function copy_files (line 336) | def copy_files(sheet, paths, dest):

FILE: visidata/sidebar.py
  class AddedHelp (line 21) | class AddedHelp:
    method __init__ (line 23) | def __init__(self, text:Union[str,'HelpPane'], title='', help_flag=''):
    method __enter__ (line 30) | def __enter__(self):
    method __exit__ (line 36) | def __exit__(self, *args):
  function formatter_helpstr (line 43) | def formatter_helpstr(sheet):
  function default_sidebar (line 49) | def default_sidebar(sheet):
  function cycleSidebar (line 57) | def cycleSidebar(vd, n:int=1):
  function help_sidebars (line 70) | def help_sidebars(sheet) -> 'list[Callable[[], tuple[str,str]]]':
  function sidebarStatus (line 80) | def sidebarStatus(vd) -> str:
  function recentStatusMessages (line 91) | def recentStatusMessages(vd) -> str:
  function drawSidebar (line 114) | def drawSidebar(vd, scr, sheet):
  function drawBox (line 136) | def drawBox(vd, scr, x, y, w, h, cattr, bottom=True):
  function drawSidebarText (line 150) | def drawSidebarText(sheet, scr, text:Union[None,str,'HelpPane'], title:s...
  class SidebarSheet (line 222) | class SidebarSheet(TextSheet):

FILE: visidata/sort.py
  function orderBy (line 8) | def orderBy(sheet, *cols, reverse=False, change_column=False, save_cmd_i...
  class Reversor (line 53) | class Reversor:
    method __init__ (line 54) | def __init__(self, obj):
    method __eq__ (line 57) | def __eq__(self, other):
    method __lt__ (line 60) | def __lt__(self, other):
  function order_string (line 63) | def order_string(sheet):
  function order_from_string (line 68) | def order_from_string(sheet, s):
  function edit_ordering (line 80) | def edit_ordering(ordering, col, reverse):
  function ordering (line 102) | def ordering(sheet) -> 'list[tuple[Column, bool]]':
  function sortkey (line 112) | def sortkey(sheet, r, ordering:'list[tuple[Column, bool]]'=[]):
  function sort (line 123) | def sort(self):
  function _sort_order (line 148) | def _sort_order(col, srccol):

FILE: visidata/statusbar.py
  function ancestors (line 35) | def ancestors(sheet):
  function sheetlist (line 42) | def sheetlist(sheet):
  function _updateStatusBeforeExec (line 67) | def _updateStatusBeforeExec(sheet, cmd, args, ks):
  function statuses (line 78) | def statuses(vd):
  function statusHistory (line 83) | def statusHistory(vd):
  function getStatusSource (line 87) | def getStatusSource(vd) -> str:
  function printStatus (line 92) | def printStatus(vd, *args, priority=0, source=None):
  function status (line 102) | def status(vd, *args, priority=0):
  function addToStatusHistory (line 118) | def addToStatusHistory(vd, *args, priority=0, source=None):
  function error (line 129) | def error(vd, *args):
  function fail (line 135) | def fail(vd, *args):
  function warning (line 141) | def warning(vd, *args):
  function aside (line 146) | def aside(vd, *args, priority=0):
  function debug (line 151) | def debug(vd, *args, **kwargs):
  function composeStatus (line 157) | def composeStatus(msgparts, n=1):
  function leftStatus (line 165) | def leftStatus(sheet):
  function drawLeftStatus (line 171) | def drawLeftStatus(vd, scr, vs):
  function rightStatus (line 198) | def rightStatus(vd, sheet):
  function keystrokeStatus (line 204) | def keystrokeStatus(vs):
  function threadStatus (line 212) | def threadStatus(vs) -> str:
  function modifiedStatus (line 227) | def modifiedStatus(sheet):
  function selectedStatus (line 235) | def selectedStatus(sheet):
  function selectedStatus (line 240) | def selectedStatus(sheet):
  function drawRightStatus (line 245) | def drawRightStatus(vd, scr, vs):
  class StatusSheet (line 264) | class StatusSheet(Sheet):
    method reload (line 279) | def reload(self):
  function statusHistorySheet (line 284) | def statusHistorySheet(vd):

FILE: visidata/stored_list.py
  class StoredList (line 7) | class StoredList(list):
    method __init__ (line 9) | def __init__(self, *args, name:str='', **kwargs):
    method path (line 14) | def path(self):
    method reload (line 22) | def reload(self):
    method append (line 38) | def append(self, v):

FILE: visidata/tests/conftest.py
  function curses_setup (line 6) | def curses_setup():
  function mock_screen (line 18) | def mock_screen():

FILE: visidata/tests/test_cliptext.py
  class TestClipText (line 8) | class TestClipText:
    method test_dispwidth (line 14) | def test_dispwidth(self, s, dispw):
    method test_clipstr (line 47) | def test_clipstr(self, s, w, clippeds, clippedw):
    method test_clipstr_wide_truncator (line 75) | def test_clipstr_wide_truncator(self, s, w, clippeds, clippedw):
    method test_clipstr_empty_truncator (line 101) | def test_clipstr_empty_truncator(self, s, w, clippeds, clippedw):
    method test_clipstr_unprintable (line 114) | def test_clipstr_unprintable(self, s, w, truncator, clippeds, clippedw):
    method test_clipstr_start (line 133) | def test_clipstr_start(self, s, w, clippeds, clippedw):
    method test_clipstr_start_truncator (line 151) | def test_clipstr_start_truncator(self, s, w, clippeds, clippedw):
    method test_clipstr_middle (line 187) | def test_clipstr_middle(self, s, w, clippeds, clippedw):
    method test_truncate_markup_middle (line 257) | def test_truncate_markup_middle(self, s, dispw, clipped):
    method test_wraptext_color_spans_lines (line 286) | def test_wraptext_color_spans_lines(self, text, width, expected):
    method test_wraptext_escaped_literals (line 300) | def test_wraptext_escaped_literals(self, text, width, expected):

FILE: visidata/tests/test_commands.py
  function isTestableCommand (line 42) | def isTestableCommand(longname, cmdlist):
  class TestCommands (line 130) | class TestCommands:
    method test_baseCommands (line 132) | def test_baseCommands(self, mock_screen):
    method runOneTest (line 175) | def runOneTest(self, mock_screen, longname):

FILE: visidata/tests/test_completer.py
  class TestCompleteExpr (line 4) | class TestCompleteExpr:
    method test_completer (line 5) | def test_completer(self):

FILE: visidata/tests/test_date.py
  class TestVisidataDate (line 7) | class TestVisidataDate:
    method test_date (line 8) | def test_date(self):  #1507

FILE: visidata/tests/test_edittext.py
  class TestEditText (line 8) | class TestEditText:
    method setUp (line 10) | def setUp(self):
    method test_keys (line 50) | def test_keys(self, mock_screen, keys, result, kwargs):

FILE: visidata/tests/test_features.py
  function pytest_generate_tests (line 4) | def pytest_generate_tests(metafunc):
  function test_feature (line 25) | def test_feature(mock_screen, testfunc):

FILE: visidata/tests/test_fixed_width.py
  class TestColumnize (line 4) | class TestColumnize:
    method test_basic (line 5) | def test_basic(self):
    method test_data_with_internal_spaces (line 10) | def test_data_with_internal_spaces(self):  #2265
    method test_empty (line 25) | def test_empty(self):
    method test_single_column (line 28) | def test_single_column(self):

FILE: visidata/tests/test_keystrokes.py
  function accumulate_keystrokes (line 5) | def accumulate_keystrokes(keys):
  function setup_custom_prefixes (line 36) | def setup_custom_prefixes():
  function test_prefix_keystrokes (line 64) | def test_prefix_keystrokes(keys, expected_outcome, expected_keystrokes):

FILE: visidata/tests/test_menu.py
  class TestMenu (line 6) | class TestMenu:
    method test_menuitems (line 7) | def test_menuitems(self):

FILE: visidata/tests/test_parsepos.py
  function test_parsePos (line 4) | def test_parsePos():

FILE: visidata/tests/test_path.py
  class TestVisidataPath (line 7) | class TestVisidataPath:
    method test_withName (line 9) | def test_withName(self):
    method test_opentwice (line 33) | def test_opentwice(self):
    method test_iterdir_yields_visidata_paths (line 40) | def test_iterdir_yields_visidata_paths(self):  # #2188
    method test_name_returns_full_filename (line 45) | def test_name_returns_full_filename(self):  # #2188
    method test_repeatfile_bytesiowrapper (line 51) | def test_repeatfile_bytesiowrapper(self):  # #2829

FILE: visidata/text_source.py
  function regex_flags (line 9) | def regex_flags(sheet):
  class FilterFile (line 14) | class FilterFile:
    method __init__ (line 15) | def __init__(self, fp, regex:str, regex_flags:int=0):
    method readline (line 20) | def readline(self) -> str:
    method __getattr__ (line 27) | def __getattr__(self, k):
    method __iter__ (line 30) | def __iter__(self):
    method __next__ (line 33) | def __next__(self):
    method __enter__ (line 39) | def __enter__(self):
    method __exit__ (line 42) | def __exit__(self, *args, **kwargs):
  function open_text_source (line 47) | def open_text_source(sheet, **kwargs):

FILE: visidata/textsheet.py
  class TextSheet (line 16) | class TextSheet(Sheet):
    method iterload (line 25) | def iterload(self):
    method readlines (line 28) | def readlines(self, source):
    method sysopen (line 38) | def sysopen(sheet, linenum=0):
  class ErrorSheet (line 58) | class ErrorSheet(TextSheet):
    method sysopen_error (line 71) | def sysopen_error(self, col, row):
    method reload (line 86) | def reload(self):
  class ErrorCellSheet (line 90) | class ErrorCellSheet(ErrorSheet):
  class ErrorsSheet (line 102) | class ErrorsSheet(Sheet):
    method reload (line 107) | def reload(self):
    method openRow (line 110) | def openRow(self, row):
  function allErrorsSheet (line 114) | def allErrorsSheet(self):
  function recentErrorsSheet (line 118) | def recentErrorsSheet(self):

FILE: visidata/theme.py
  function run (line 12) | def run(vd, *args, **kwargs):
  function set_theme (line 20) | def set_theme(obj, theme=''):

FILE: visidata/threads.py
  class QueuedFunc (line 27) | class QueuedFunc:
    method __init__ (line 28) | def __init__(self, func, args, kwargs, readonly=False):
    method _run_sync (line 36) | def _run_sync(self):
    method _run (line 39) | def _run(self):
  function _queueFunc (line 44) | def _queueFunc(vd, func, *args, _readonly=False, **kwargs):
  function _runToCapacity (line 52) | def _runToCapacity(vd):
  function asynccache (line 61) | def asynccache(keyfunc=lambda *args, **kwargs: str(args)+str(kwargs)):
  class _Progress (line 77) | class _Progress:
    method __init__ (line 78) | def __init__(self, iterable=None, gerund="", total=None, sheet=None):
    method __enter__ (line 91) | def __enter__(self):
    method addProgress (line 96) | def addProgress(self, n):
    method __exit__ (line 101) | def __exit__(self, exc_type, exc_val, tb):
    method __iter__ (line 105) | def __iter__(self):
  function Progress (line 112) | def Progress(vd, iterable=None, gerund="", total=None, sheet=None):
  function cancelThread (line 124) | def cancelThread(vd, *threads, exception=EscapeException):
  class ThreadsSheet (line 132) | class ThreadsSheet(Sheet):
    method reload (line 142) | def reload(self):
    method openRow (line 145) | def openRow(self, row):
  function elapsed_s (line 152) | def elapsed_s(t):
  function checkMemoryUsage (line 157) | def checkMemoryUsage(vd):
  function progressMade (line 190) | def progressMade(self):
  function progressTotal (line 194) | def progressTotal(self):
  function progressPct (line 199) | def progressPct(sheet):
  function _annotate_thread (line 207) | def _annotate_thread(t, endTime=None):
  function execSync (line 219) | def execSync(vd, func, *args, sheet=None, **kwargs):
  function execAsync (line 227) | def execAsync(vd, func, *args, **kwargs):
  function _toplevelTryFunc (line 264) | def _toplevelTryFunc(func, *args, **kwargs):
  function asyncignore (line 290) | def asyncignore(func):
  function asyncsingle (line 302) | def asyncsingle(func):
  function asyncsingle_queue (line 324) | def asyncsingle_queue(func):
  function unfinishedThreads (line 346) | def unfinishedThreads(self):
  function sync (line 352) | def sync(self, *joiningThreads):
  function open_pyprof (line 378) | def open_pyprof(vd, p):
  function toggleProfiling (line 384) | def toggleProfiling(vd):
  class ThreadProfiler (line 398) | class ThreadProfiler:
    method __init__ (line 399) | def __init__(self, thread):
    method __enter__ (line 403) | def __enter__(self):
    method __exit__ (line 411) | def __exit__(self, exc_type, exc_val, tb):
  class ProfileSheet (line 428) | class ProfileSheet(Sheet):
    method reload (line 454) | def reload(self):
    method openRow (line 468) | def openRow(self, row):
    method openCell (line 474) | def openCell(self, col, row):
  class ProfileStatsSheet (line 482) | class ProfileStatsSheet(Sheet):
    method reload (line 494) | def reload(self):
    method openRow (line 497) | def openRow(self, row):
  function codestr (line 500) | def codestr(code):
  function allThreadsSheet (line 507) | def allThreadsSheet(self):
  function cancel_sheet (line 511) | def cancel_sheet(sheet):

FILE: visidata/tuiwin.py
  function subwindow (line 7) | def subwindow(vd, scr, x, y, w, h):
  function getrootxy (line 15) | def getrootxy(vd, scr):  # like scr.getparyx() but for all ancestor scrs

FILE: visidata/type_currency.py
  function currency (line 10) | def currency(*args):
  function displayer_currency (line 18) | def displayer_currency(col, dw, width=None):

FILE: visidata/type_date.py
  function date_parse (line 6) | def date_parse(vd):
  class date (line 34) | class date(datetime.datetime):
    method __new__ (line 37) | def __new__(cls, *args, **kwargs):
    method __lt__ (line 59) | def __lt__(self, b):
    method __gt__ (line 64) | def __gt__(self, b):
    method __le__ (line 69) | def __le__(self, b):
    method __ge__ (line 74) | def __ge__(self, b):
    method __eq__ (line 79) | def __eq__(self, b):
    method __str__ (line 84) | def __str__(self):
    method __hash__ (line 87) | def __hash__(self):
    method __float__ (line 90) | def __float__(self):
    method __radd__ (line 93) | def __radd__(self, n):
    method __add__ (line 96) | def __add__(self, n):
    method __sub__ (line 102) | def __sub__(self, n):
  class datedelta (line 111) | class datedelta(datetime.timedelta):
    method __float__ (line 112) | def __float__(self):

FILE: visidata/type_floatsi.py
  function SIFormatter (line 7) | def SIFormatter(vd, fmtstr, val):
  function floatsi (line 21) | def floatsi(*args):

FILE: visidata/undo.py
  function isUndoableCommand (line 12) | def isUndoableCommand(longname):
  function addUndo (line 19) | def addUndo(vd, undofunc, *args, **kwargs):
  function undo (line 39) | def undo(vd, sheet):
  function redo (line 65) | def redo(vd, sheet):
  function undoAttrFunc (line 72) | def undoAttrFunc(objs, attrname):
  class Fanout (line 81) | class Fanout(list):
    method __getattr__ (line 83) | def __getattr__(self, k):
    method __setattr__ (line 86) | def __setattr__(self, k, v):
    method __call__ (line 91) | def __call__(self, *args, **kwargs):
  function undoAttrCopyFunc (line 95) | def undoAttrCopyFunc(objs, attrname):
  function addUndoSetValues (line 105) | def addUndoSetValues(vd, cols, rows):

FILE: visidata/utils.py
  class AlwaysDict (line 11) | class AlwaysDict(dict):
    method __init__ (line 13) | def __init__(self, val, **kwargs):
    method __getitem__ (line 17) | def __getitem__(self, k):
  class AttrDict (line 20) | class AttrDict(dict):
    method __getattr__ (line 22) | def __getattr__(self, k):
    method __setattr__ (line 33) | def __setattr__(self, k, v):
    method __dir__ (line 36) | def __dir__(self):
  class DefaultAttrDict (line 40) | class DefaultAttrDict(dict):
    method __getattr__ (line 42) | def __getattr__(self, k):
    method __setattr__ (line 49) | def __setattr__(self, k, v):
    method __dir__ (line 52) | def __dir__(self):
  class classproperty (line 58) | class classproperty(property):
    method __get__ (line 59) | def __get__(self, cls, obj):
  function moveListItem (line 63) | def moveListItem(L, fromidx, toidx):
  function setitem (line 72) | def setitem(r, i, v):  # function needed for use in lambda
  function getitem (line 76) | def getitem(o, k, default=None):
  function getitemdef (line 79) | def getitemdef(o, k, default=None):
  function getattrdeep (line 86) | def getattrdeep(obj, attr, *default, getter=getattr):
  function setattrdeep (line 110) | def setattrdeep(obj, attr, val, getter=getattr, setter=setattr):
  function getitemdeep (line 131) | def getitemdeep(obj, k, *default):
  function setitemdeep (line 139) | def setitemdeep(obj, k, val):
  function namedlist (line 143) | def namedlist(objname, fieldnames):
  class ExplodingMock (line 175) | class ExplodingMock:
    method __init__ (line 177) | def __init__(self, msg):
    method __getattr__ (line 180) | def __getattr__(self, k):
    method __bool__ (line 183) | def __bool__(self):
  class MissingAttrFormatter (line 187) | class MissingAttrFormatter(string.Formatter):
    method get_value (line 189) | def get_value(self, key, args, kwargs):
    method get_field (line 195) | def get_field(self, field_name, args, kwargs):
    method format_field (line 201) | def format_field(self, value, format_spec):
  function ScopedSetattr (line 211) | def ScopedSetattr(obj, attrname, val):
  function colname_letters (line 219) | def colname_letters(num):

FILE: visidata/vdobj.py
  function asyncthread (line 16) | def asyncthread(func):
  class VisiData (line 27) | class VisiData(visidata.Extensible):
    method global_api (line 31) | def global_api(cls, func):
    method __init__ (line 39) | def __init__(self):
    method cursesEnabled (line 54) | def cursesEnabled(self):
    method sheetstack (line 57) | def sheetstack(self, pane=0):
    method stackedSheets (line 65) | def stackedSheets(self):
    method activeSheet (line 69) | def activeSheet(self):
    method activeStack (line 82) | def activeStack(self):
    method __copy__ (line 85) | def __copy__(self):
    method finalInit (line 89) | def finalInit(self):
    method init (line 94) | def init(cls, membername, initfunc, **kwargs):
    method clearCaches (line 103) | def clearCaches(self):
    method resetVisiData (line 107) | def resetVisiData(self):
    method get_wch (line 118) | def get_wch(self, scr):
    method drainPendingKeys (line 127) | def drainPendingKeys(self, scr):
    method getkeystroke (line 144) | def getkeystroke(self, scr, vs=None):
    method screenHeight (line 176) | def screenHeight(self):
    method screenWidth (line 180) | def screenWidth(self):
    method activeCommand (line 184) | def activeCommand(self):
    method activeCommand (line 194) | def activeCommand(self, value):

FILE: visidata/vendor/appdirs.py
  function user_data_dir (line 44) | def user_data_dir(appname=None, appauthor=None, version=None, roaming=Fa...
  function site_data_dir (line 99) | def site_data_dir(appname=None, appauthor=None, version=None, multipath=...
  function user_config_dir (line 165) | def user_config_dir(appname=None, appauthor=None, version=None, roaming=...
  function site_config_dir (line 209) | def site_config_dir(appname=None, appauthor=None, version=None, multipat...
  function user_cache_dir (line 264) | def user_cache_dir(appname=None, appauthor=None, version=None, opinion=T...
  function user_state_dir (line 321) | def user_state_dir(appname=None, appauthor=None, version=None, roaming=F...
  function user_log_dir (line 363) | def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
  class AppDirs (line 414) | class AppDirs(object):
    method __init__ (line 416) | def __init__(self, appname=None, appauthor=None, version=None,
    method user_data_dir (line 425) | def user_data_dir(self):
    method site_data_dir (line 430) | def site_data_dir(self):
    method user_config_dir (line 435) | def user_config_dir(self):
    method site_config_dir (line 440) | def site_config_dir(self):
    method user_cache_dir (line 445) | def user_cache_dir(self):
    method user_state_dir (line 450) | def user_state_dir(self):
    method user_log_dir (line 455) | def user_log_dir(self):
  function _get_win_folder_from_registry (line 462) | def _get_win_folder_from_registry(csidl_name):
  function _get_win_folder_with_ctypes (line 486) | def _get_win_folder_with_ctypes(csidl_name):
  function _get_win_folder_with_jna (line 512) | def _get_win_folder_with_jna(csidl_name):
  function _get_win_folder_from_environ (line 538) | def _get_win_folder_from_environ(csidl_name):

FILE: visidata/wrappers.py
  function isNullFunc (line 10) | def isNullFunc(sheet):
  class TypedWrapper (line 19) | class TypedWrapper:
    method __init__ (line 20) | def __init__(self, func, *args):
    method __bool__ (line 25) | def __bool__(self):
    method __len__ (line 28) | def __len__(self):
    method __repr__ (line 31) | def __repr__(self):
    method __str__ (line 34) | def __str__(self):
    method __lt__ (line 40) | def __lt__(self, x):
    method __add__ (line 44) | def __add__(self, x):
    method __radd__ (line 47) | def __radd__(self, x):
    method __hash__ (line 50) | def __hash__(self):
    method __eq__ (line 53) | def __eq__(self, x):
    method __iter__ (line 57) | def __iter__(self):
    method __next__ (line 60) | def __next__(self):
  class TypedExceptionWrapper (line 64) | class TypedExceptionWrapper(TypedWrapper):
    method __init__ (line 65) | def __init__(self, func, *args, exception=None):
    method __str__ (line 71) | def __str__(self):
    method __hash__ (line 74) | def __hash__(self):
    method __eq__ (line 77) | def __eq__(self, x):
  function forward (line 82) | def forward(wr):
  function wrmap (line 88) | def wrmap(func, iterable, *args):
  function wrapply (line 97) | def wrapply(func, *args, **kwargs):
Condensed preview — 995 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (7,122K chars).
[
  {
    "path": ".codespellrc",
    "chars": 267,
    "preview": "[codespell]\nskip = .git,*.pdf,*.svg,*.tsv,man,formats.jsonl,sample_data,*.cast,golden\n# some ad-hoc or less common word "
  },
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 14,
    "preview": "FROM python:3\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 170,
    "preview": "{\n    \"name\": \"VisiData\",\n    \"dockerFile\": \"Dockerfile\",\n    \"extensions\": [\n        \"ms-python.python\"\n    ],\n    \"pos"
  },
  {
    "path": ".devcontainer/launch.json",
    "chars": 601,
    "preview": "{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Python: vd launch\",\n            \"type\":"
  },
  {
    "path": ".devcontainer/post-create.sh",
    "chars": 43,
    "preview": "#!/bin/bash\n\nmake install-dev setup-vscode\n"
  },
  {
    "path": ".devcontainer/settings.json",
    "chars": 247,
    "preview": "{\n    \"python.pythonPath\": \"/usr/local/bin/python\",\n    \"python.testing.pytestArgs\": [\n        \"visidata\"\n    ],\n    \"py"
  },
  {
    "path": ".gitattributes",
    "chars": 31,
    "preview": "www/asciinema-player.js binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 31,
    "preview": "github: saulpw\npatreon: saulpw\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1072,
    "preview": "---\nname: Bug report\nabout: Create an issue if anything doesn't appear to be working right.\ntitle: ''\nlabels: bug\nassign"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "chars": 594,
    "preview": "---\nname: Feature request\nabout: Provide feedback on how you want VisiData to grow in the future.\ntitle: '[] '\nlabels: w"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/loader-request.md",
    "chars": 542,
    "preview": "---\nname: Loader request\nabout: Suggest a loader that supports a new data format\ntitle: \"[loader] \"\nlabels: feature requ"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "chars": 506,
    "preview": "---\nname: Question\nabout: Ask a question you have about VisiData.\ntitle: '[question]'\nlabels: question\nassignees: ''\n\n--"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 251,
    "preview": "- [ ] If contributing a core loader, [the loader checklist](https://visidata.org/docs/contributing#loader) was reference"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1128,
    "preview": "name: visidata-ci-build\non:\n  pull_request:\n    branches:\n      - stable\n      - develop\n  push:\n    branches:\n    - sta"
  },
  {
    "path": ".github/workflows/vdsql.yml",
    "chars": 689,
    "preview": "name: vdsql-testing\non:\n  pull_request:\n    branches:\n      - stable\n      - develop\n  push:\n    branches:\n      - stabl"
  },
  {
    "path": ".github/workflows/vgit.yml",
    "chars": 839,
    "preview": "name: vgit-testing\non:\n  pull_request:\n    branches:\n      - stable\n      - develop\n  push:\n    branches:\n      - stable"
  },
  {
    "path": ".gitignore",
    "chars": 292,
    "preview": "__pycache__\n.tox/\n.idea/\n.vscode/\nvisidata.egg-info/\n*.egg-info/\n*v_env*\n*~\n*.swp\n*.swo\n*dist\n*_build\nbuild/\ntags\n/tests"
  },
  {
    "path": ".gitmodules",
    "chars": 205,
    "preview": "[submodule \"deps/pyxlsb\"]\n\tpath = deps/pyxlsb\n\turl = https://github.com/saulpw/pyxlsb.git\n\n[submodule \"deps/requests-cac"
  },
  {
    "path": ".gitpod.Dockerfile",
    "chars": 14,
    "preview": "FROM python:3\n"
  },
  {
    "path": ".gitpod.yml",
    "chars": 114,
    "preview": "image:\n  file: .gitpod.Dockerfile\n\ntasks:\n  - init: pip install -r ./dev/requirements-dev.txt && pip install -e .\n"
  },
  {
    "path": ".mailmap",
    "chars": 1260,
    "preview": "# .mailmap - Consolidate git author identities\n# See: https://git-scm.com/docs/gitmailmap\n#\n# Format: Canonical Name <ca"
  },
  {
    "path": ".theia/settings.json",
    "chars": 197,
    "preview": "{\n    \"python.testing.pytestArgs\": [\n        \"visidata\"\n    ],\n    \"python.testing.unittestEnabled\": false,\n    \"python."
  },
  {
    "path": "AGENTS.md",
    "chars": 2176,
    "preview": "# Repository Guidelines\n\n## Start Here (Progressive Disclosure)\n- Read `CLAUDE.md` first for the primary contributor wor"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 173897,
    "preview": "# VisiData version history\n\n# v3.4 (unreleased)\n\nThanks to @midichef for many bugfixes and improvements.\n\n## New Feature"
  },
  {
    "path": "CLAUDE.md",
    "chars": 4410,
    "preview": "# VisiData Development Guide\n\nQuick reference for VisiData development. For detailed coding patterns, conventions, and b"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 25,
    "preview": "Please don't be a dick ♥\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 5362,
    "preview": "# Contributing\n\n## Spread the Word\n\nThe single best way you can contribute, is to share your enthusiasm about VisiData w"
  },
  {
    "path": "Dockerfile.alpine",
    "chars": 215,
    "preview": "FROM python:3.8-alpine\n\nRUN pip install requests python-dateutil wcwidth\n\nRUN mkdir -p /opt/visidata\nWORKDIR /opt/visida"
  },
  {
    "path": "Dockerfile.darkdraw.alpine",
    "chars": 224,
    "preview": "FROM visidata\n\nRUN apk add git\nRUN pip install git+https://github.com/devottys/darkdraw.git@master\nRUN sh -c \"echo >>~/."
  },
  {
    "path": "LICENSE.gpl3",
    "chars": 35147,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "MANIFEST.in",
    "chars": 818,
    "preview": "include README.md\ninclude LICENSE.gpl3\ninclude CHANGELOG.md\ninclude CONTRIBUTING.md\ninclude CODE_OF_CONDUCT.md\ninclude r"
  },
  {
    "path": "Makefile",
    "chars": 2002,
    "preview": ".PHONY: help \\\n       install install-dev install-test install-all \\\n       test test-all test-vgit test-vdsql \\\n       "
  },
  {
    "path": "README.md",
    "chars": 3325,
    "preview": "# VisiData v3.3\n\n[![Tests](https://github.com/saulpw/visidata/workflows/visidata-ci-build/badge.svg)](https://github.com"
  },
  {
    "path": "bin/filter-doc.py",
    "chars": 760,
    "preview": "#!/usr/bin/python3\n\nimport fileinput\nimport inspect\nimport visidata\n\ntmpl='''\n~~~\n    {name}{sign} [ [{sourcepy}:{source"
  },
  {
    "path": "bin/vd",
    "chars": 100,
    "preview": "#!/usr/bin/env python3\n\nimport visidata.main\n\nif __name__ == '__main__':\n    visidata.main.vd_cli()\n"
  },
  {
    "path": "bin/vd2to3.vdx",
    "chars": 183,
    "preview": "#!/usr/bin/env -S vd -p\n# VisiData v3.0dev\nopen-file ~/.visidata/macros.tsv\ncol command\nrename-col binding\ncol filename\n"
  },
  {
    "path": "bin/viewtsv.py",
    "chars": 1142,
    "preview": "#!/usr/bin/env python3\n\n# as plugin: `from viewtsv import open_tsv` in .visidatarc\n# standalone: `viewtsv.py <tsv-files>"
  },
  {
    "path": "dev/DOCS.md",
    "chars": 3033,
    "preview": "# VisiData Documentation Style Guide\n\n`docs/man.md` is generated from the manpage source (`visidata/man/vd.inc`) by `dev"
  },
  {
    "path": "dev/GIT.md",
    "chars": 4912,
    "preview": "# Git and Version Control Practices\n\nThis document covers VisiData's conventions for Git commits, issue tracking, and ve"
  },
  {
    "path": "dev/OPTIONS.md",
    "chars": 3702,
    "preview": "# VisiData Options System\n\nReference for how options work in VisiData. Use this when working with options on sheets, pat"
  },
  {
    "path": "dev/PERFORMANCE.md",
    "chars": 4264,
    "preview": "# How to analyze performance issues in VisiData\n\n## 1. Find a reliable repro (usually a large or pathological dataset).\n"
  },
  {
    "path": "dev/README.md",
    "chars": 2332,
    "preview": "# dev/\n\nResources for VisiData contributors and maintainers.\n\n## Contributor Guides\n\n| File | Description |\n|------|----"
  },
  {
    "path": "dev/STYLE.md",
    "chars": 5573,
    "preview": "# VisiData Coding Style\n\n## Naming Conventions\n\n- **`camelCaps`** — execstr API: `openRow()`, `cursorRow`, `selectedRows"
  },
  {
    "path": "dev/TESTING.md",
    "chars": 11252,
    "preview": "# Testing\n\n## Running all tests\n\n`dev/test-all.sh` is the top-level test runner. It discovers and runs all `tests/test-*"
  },
  {
    "path": "dev/build-container",
    "chars": 194,
    "preview": "#!/usr/bin/env sh\n\nset -u\n\ncd \"$(git rev-parse --show-toplevel)\" # begin in project root\ndocker build -f Dockerfile.alpi"
  },
  {
    "path": "dev/checklists/add-aggregator.md",
    "chars": 419,
    "preview": "- API for adding aggregators can be found [here](https://www.visidata.org/docs/api/columns.html#aggregators). A descript"
  },
  {
    "path": "dev/checklists/add-command.md",
    "chars": 2704,
    "preview": "# Command checklist\n\n## Writing the command\n1) choose where the command will go (module? visidatarc?);\n    - most comman"
  },
  {
    "path": "dev/checklists/feature.md",
    "chars": 931,
    "preview": "\n# Feature Checklist\n\n- [ ] user docs in docs/\n- [ ] API docs in docs/api/\n- [ ] release notes (in changelog until relea"
  },
  {
    "path": "dev/checklists/manual-tests.md",
    "chars": 4535,
    "preview": "# Functionality to test manual\n1. cmdlog + replay\n    - logging of options\n    - logging of rows and columns\n    - if em"
  },
  {
    "path": "dev/checklists/release.md",
    "chars": 4912,
    "preview": "# Release process for the next `stable` version\n\n1. Merge `stable` to `develop` (if necessary)\n\n2. Verify that documenta"
  },
  {
    "path": "dev/debian/changelog",
    "chars": 1496,
    "preview": "visidata (2.8-1) unstable; urgency=low\n\n  * Update package to 2.8\n\n -- Anja Boskovic <anja.kefala@gmail.com>  Wed, 15 De"
  },
  {
    "path": "dev/debian/control",
    "chars": 858,
    "preview": "Source: visidata\nSection: devel\nPriority: optional\nMaintainer: Anja Boskovic <anja.kefala@gmail.com>\nBuild-Depends: debh"
  },
  {
    "path": "dev/debian/copyright",
    "chars": 564,
    "preview": "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: VisiData\nUpstream-Contact: Saul"
  },
  {
    "path": "dev/debian/manpages",
    "chars": 18,
    "preview": "visidata/man/vd.1\n"
  },
  {
    "path": "dev/debian/rules",
    "chars": 117,
    "preview": "#!/usr/bin/make -f\n\nexport DH_VERBOSE=1\nexport PYBUILD_NAME=visidata\n\n%:\n\tdh $@ --with python3 --buildsystem=pybuild\n"
  },
  {
    "path": "dev/debian/source/format",
    "chars": 12,
    "preview": "3.0 (quilt)\n"
  },
  {
    "path": "dev/debian/source/local-options",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "dev/debian/upstream/metadata",
    "chars": 223,
    "preview": "---\nBug-Database: https://github.com/saulpw/visidata/issues\nBug-Submit: https://github.com/saulpw/visidata/issues/new\nRe"
  },
  {
    "path": "dev/debian/watch",
    "chars": 141,
    "preview": "version=3\nopts=uversionmangle=s/(rc|a|b|c)/~$1/ \\\nhttps://pypi.debian.net/visidata/visidata-(.+)\\.(?:zip|tgz|tbz|txz|(?:"
  },
  {
    "path": "dev/design/000-notes.md",
    "chars": 3769,
    "preview": "# initial design notes [2016-10-24]\n\na. one sheet per screen; no panes\nb. rapidly switch between two sheets out of dozen"
  },
  {
    "path": "dev/design/160-longnames.md",
    "chars": 2214,
    "preview": "# Command longname design and checklist\n\n1) two words max if possible, should be short and fit on a keymap (verb - objec"
  },
  {
    "path": "dev/design/169-settings.md",
    "chars": 3587,
    "preview": "# 169-settings: Commands, Keybindings, and Options\n\n## I. Layers of settings\n\n- a. Settings are named globally uniquely."
  },
  {
    "path": "dev/design/173-benchmark.md",
    "chars": 777,
    "preview": "# Benchmark\n\nUsing an interactive tool or the commandline (no scripts):\n\n1. load/parse the dataset\n2. clean various issu"
  },
  {
    "path": "dev/design/174-keycols.md",
    "chars": 528,
    "preview": "# Key Columns\n\n- key columns must be visible\n- hiding a key column also makes it a non-key column\n- with \"keycol\" is a p"
  },
  {
    "path": "dev/design/175-design-terms.md",
    "chars": 803,
    "preview": "# Types of design statements\n\n1) there are how it already is, which are by design and implemented correctly ('is', 'are'"
  },
  {
    "path": "dev/design/176-miscrules.md",
    "chars": 258,
    "preview": "# Miscellaneous rules discovered during implementation\n\n- Don't put an @asynccache on calcValue, unless you know the row"
  },
  {
    "path": "dev/design/181-benchmark-data.md",
    "chars": 1626,
    "preview": "# Benchmark Dataset\n\n- a coherent, grounded, interesting dataset--probably fictional/manufactured\n   - multi-culti/topic"
  },
  {
    "path": "dev/design/232-input.md",
    "chars": 1235,
    "preview": "# getting input from the user\n\n    VisiData.input(prompt, type='', defaultLast=False, **editargs) -> str:\n      - get a "
  },
  {
    "path": "dev/design/260-push.md",
    "chars": 449,
    "preview": "- first push causes reload (self.rows is initially the UNLOADED sentinel)\n- reload is usually async\n- reload assigns a n"
  },
  {
    "path": "dev/design/README.md",
    "chars": 1692,
    "preview": "# Design Docs\n\nArchitecture and feature design documents for VisiData. See also [175-design-terms.md](175-design-terms.m"
  },
  {
    "path": "dev/design/path.md",
    "chars": 3018,
    "preview": "# visidata.Path\n\n- contains pathlib.Path as `._path` member\n   -  `__getattr__` forwards to `.path` so interfaces are ve"
  },
  {
    "path": "dev/diff-test.sh",
    "chars": 182,
    "preview": "#!/bin/bash\n\nfor fn in `git diff --name-only -- *.tsv` ; do\n    if [ \"${fn%-notest.tsv}-notest\" != \"${fn%.tsv}\" ]\n    th"
  },
  {
    "path": "dev/formats.jsonl",
    "chars": 17124,
    "preview": "{\"filetype\": \"csv\", \"filetype_url\": \"#csv\", \"aliases\": \"\", \"requirements\": \"\", \"format\": \"Comma-Separated Values\", \"load"
  },
  {
    "path": "dev/formats.vd",
    "chars": 860,
    "preview": "sheet\tcol\trow\tlongname\tinput\tkeystrokes\tcomment\n\tSqliteSheet\theader\tset-option\t0\t\t\n\t\t\topen-file\tdev/formats.jsonl\to\t\nfor"
  },
  {
    "path": "dev/hooks/pre-push",
    "chars": 89,
    "preview": "#!/usr/bin/env bash\n\n# Pre-push hook: run all tests before pushing\n\nset -e\nmake test-all\n"
  },
  {
    "path": "dev/mkman.sh",
    "chars": 1214,
    "preview": "#!/bin/bash\n\n# Usage: $0\n#    builds vd(1) man page in src repo (run via `make man`)\n\n# TODO:\n#   - parse_options should"
  },
  {
    "path": "dev/mkpandas-df.py",
    "chars": 1794,
    "preview": "#!/usr/bin/env python3\n\"\"\"\nAuthor: @azjps\nRun visidata PandasSheet on a dataframe with columns\nof different datatypes an"
  },
  {
    "path": "dev/quit.vdx",
    "chars": 9,
    "preview": "quit-all\n"
  },
  {
    "path": "dev/requirements-dev.txt",
    "chars": 27,
    "preview": "markdown\npytest\npyyaml\ntox\n"
  },
  {
    "path": "dev/run-tests-individually.sh",
    "chars": 1508,
    "preview": "#!/usr/bin/env bash\n# usage: ./dev/run-tests-individually.sh [<tests/name.vd> […]]\n#\n# Runs each test in its own vd proc"
  },
  {
    "path": "dev/test-all.sh",
    "chars": 636,
    "preview": "#!/usr/bin/env bash\n# Run test scripts with labeled output\n# Usage: test-all.sh [test scripts...]\n\nsource tests/testenv."
  },
  {
    "path": "dev/test-stdin-replay.vdx",
    "chars": 22,
    "preview": "open-file -\ntranspose\n"
  },
  {
    "path": "dev/test.sh",
    "chars": 146,
    "preview": "#!/usr/bin/env bash\n# Wrapper for tests/test-vdx.sh (cmdlog golden tests)\n# Usage: test.sh [-d] [-j N] [testname ...]\nex"
  },
  {
    "path": "dev/types.jsonl",
    "chars": 750,
    "preview": "{\"type\": \"anytype\", \"description\": \"pass-through\", \"numeric\": \"\", \"command\": \"type-anytype\", \"keystrokes\": \"z~\"}\n{\"type\""
  },
  {
    "path": "dev/vduplot.vdx",
    "chars": 374,
    "preview": "#!/usr/bin/env vd -p\nopen-file http://visidata.org/usage.tsv\ncol daily_users\ntype-int\naggregate-col sum\ncol motd_fetches"
  },
  {
    "path": "dev/visidata-brew.rb",
    "chars": 3690,
    "preview": "class Visidata < Formula\n  include Language::Python::Virtualenv\n  desc \"Terminal utility for exploring and arranging tab"
  },
  {
    "path": "dev/workshop-outline.md",
    "chars": 1480,
    "preview": "# Intro\n   - terminal \"spreadsheet\"\n   - python3, \\*no dependencies\n   - visidata 1.0 made during 2017 sabbatical\n   - G"
  },
  {
    "path": "dev/zsh-completion.in",
    "chars": 476,
    "preview": "#compdef vd visidata\n\n(( $+functions[_vd_files] )) ||\n_vd_files () {\n  case $PREFIX in\n    (*) _files $* ;;\n  esac\n  cas"
  },
  {
    "path": "dev/zsh-completion.py",
    "chars": 1818,
    "preview": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport os\nfrom os.path import dirname as dirn\nimport sys\n"
  },
  {
    "path": "docs/README.md",
    "chars": 3321,
    "preview": "# VisiData Documentation Index\n\nGuide to what each file covers, so it's clear which files to update when user-facing beh"
  },
  {
    "path": "docs/api/Makefile",
    "chars": 634,
    "preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
  },
  {
    "path": "docs/api/_static/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/api/_static/css/custom.css",
    "chars": 307,
    "preview": "dl.function dt, dl.data dt, dl.class dt, dl.attribute dt {\n    margin-top: 1.5em;\n    margin-bottom: .5em;\n    padding: "
  },
  {
    "path": "docs/api/async.rst",
    "chars": 5811,
    "preview": ".. _threads:\n\n=========================\nThreads\n=========================\n\nVisiData is not necessarily the fastest at pr"
  },
  {
    "path": "docs/api/canvas.rst",
    "chars": 914,
    "preview": "Canvas\n=========\n\n.. autofunction visidata.BoundingBox\n\n.. autofunction visidata.Canvas.point\n.. autofunction visidata.C"
  },
  {
    "path": "docs/api/columns.rst",
    "chars": 8527,
    "preview": ".. _columns:\n\nColumns\n====================================\n\nColumns are the heart of the VisiData computation engine.\n\nE"
  },
  {
    "path": "docs/api/commands.rst",
    "chars": 5565,
    "preview": ".. _commands:\n\nCommands\n--------\n\nVisiData is **command-driven**, which means that it only does something when you tell "
  },
  {
    "path": "docs/api/conf.py",
    "chars": 2209,
    "preview": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common op"
  },
  {
    "path": "docs/api/data.rst",
    "chars": 1723,
    "preview": "Selected Rows\n-------------\n\nEach TableSheet has a set of *selected rows*, which is a strict subset of the rows on the s"
  },
  {
    "path": "docs/api/extensible.rst",
    "chars": 6480,
    "preview": "API Extensions\n===============\n\nOne of VisiData's core design goals is **extensibility**.\nMany of its features can exist"
  },
  {
    "path": "docs/api/guides.rst",
    "chars": 2988,
    "preview": "Guides\n=======\n\nA Guide is an in-app writeup of a particular feature that gives a basic synopsis of how to use it and ex"
  },
  {
    "path": "docs/api/index.rst",
    "chars": 1582,
    "preview": ".. VisiData documentation master file, created by\n   sphinx-quickstart on Wed Sep 23 19:53:19 2020.\n   You can adapt thi"
  },
  {
    "path": "docs/api/interface.rst",
    "chars": 5566,
    "preview": "Drawing\n========\n\nThese functions should rarely be necessary to use.\n``BaseSheet.draw`` must be overridden on non-tabula"
  },
  {
    "path": "docs/api/loaders.rst",
    "chars": 15880,
    "preview": "Loaders\n=======\n\n.. note::\n\n    You are welcome to submit new loaders to core VisiData, or as plugins. Please, see our `"
  },
  {
    "path": "docs/api/make.bat",
    "chars": 795,
    "preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
  },
  {
    "path": "docs/api/modify.rst",
    "chars": 3471,
    "preview": "===============\nModifying Data\n===============\n\n\nWhen adding rows, deleting rows, or modifying cell values, **always use"
  },
  {
    "path": "docs/api/modules.rst",
    "chars": 1444,
    "preview": "\n# Module abilities\n\nInternal modules and external plugins can:\n\n- define new commands\n- add new options\n- set existing "
  },
  {
    "path": "docs/api/options.rst",
    "chars": 5184,
    "preview": "Options\n========\n\nAdding to the :ref:`hello world<hello-world>` example from the intro, the displayed text could be made"
  },
  {
    "path": "docs/api/plugins.rst",
    "chars": 1887,
    "preview": "Plugins\n========\n\n\nPlugin file structure\n----------------------\n\nMost features can be self-contained in their own .py fi"
  },
  {
    "path": "docs/api/requirements.txt",
    "chars": 110,
    "preview": "recommonmark\nsphinx\nsphinx-markdown-tables\nsphinx-argparse\ngit+https://github.com/saulpw/visidata.git@develop\n"
  },
  {
    "path": "docs/api/runtime.txt",
    "chars": 4,
    "preview": "3.7\n"
  },
  {
    "path": "docs/api/sheets.rst",
    "chars": 2609,
    "preview": ".. _sheets:\n\nSheets\n====================================\n\nA Sheet is a representation of some source, usually a :ref:`Pa"
  },
  {
    "path": "docs/api/style.rst",
    "chars": 2783,
    "preview": "Guide Style Guide\n=================\n\nGeneral Notes\n-------------\n\n- Review first sentences `with this in mind <https://j"
  },
  {
    "path": "docs/casts/expand-cols.cast",
    "chars": 29357,
    "preview": "{\"version\": 2, \"width\": 64, \"height\": 11, \"timestamp\": 1593233558, \"env\": {\"SHELL\": \"/Users/akerrigan/.local/homebrew/bi"
  },
  {
    "path": "docs/casts/pivot-graphs.cast",
    "chars": 143584,
    "preview": "{\"width\":80,\"version\":2,\"height\":25,\"env\":{\"TERM\":\"xterm-256color\",\"SHELL\":\"/usr/bin/zsh\"},\"command\":\"vd -p cmdlog-1.vd "
  },
  {
    "path": "docs/casts/pivot.cast",
    "chars": 47852,
    "preview": "{\"width\":80,\"version\":2,\"height\":25,\"env\":{\"TERM\":\"xterm-256color\",\"SHELL\":\"/usr/bin/zsh\"},\"command\":\"bin/vd -p tests/pi"
  },
  {
    "path": "docs/casts/save-restore.cast",
    "chars": 64500,
    "preview": "{\"width\":80,\"version\":2,\"height\":25,\"env\":{\"TERM\":\"xterm-256color\",\"SHELL\":\"/usr/bin/zsh\"}}\n[0.124452,\"o\",\"\\u001B[1m\\u00"
  },
  {
    "path": "docs/casts/split-regex.cast",
    "chars": 175963,
    "preview": "{\"width\":80,\"version\":2,\"height\":25,\"env\":{\"TERM\":\"xterm-256color\",\"SHELL\":\"/usr/bin/zsh\"},\"command\":\"vd xd-metadata.zip"
  },
  {
    "path": "docs/casts/types.cast",
    "chars": 91743,
    "preview": "{\"width\":80,\"version\":2,\"height\":25,\"env\":{\"TERM\":\"xterm-256color\",\"SHELL\":\"/usr/bin/zsh\"}}\n[0.156876,\"o\",\"\\u001B[1m\\u00"
  },
  {
    "path": "docs/colors.md",
    "chars": 7828,
    "preview": "---\neleventyNavigation:\n    key: Customizing VisiData - Colors\n    order: 12\nupdate: 2023-10-12\nversion: VisiData v3.0\n-"
  },
  {
    "path": "docs/columns.md",
    "chars": 17719,
    "preview": "---\neleventyNavigation:\n    key: Columns\n    order: 5\nupdate: 2021-11-01\nversion: VisiData 2.6\n---\n\n## How to manipulate"
  },
  {
    "path": "docs/contributing.md",
    "chars": 1598,
    "preview": "---\neleventyNavigation:\n  key: Checklists for Contributing to VisiData\n  order: 99\n---\n\n## [Submitting a Core Loader](#l"
  },
  {
    "path": "docs/crud.md",
    "chars": 1087,
    "preview": "---\neleventyNavigation:\n    key: Creating sheets, rows and columns\n    order: 8\nUpdate: 2021-11-18\nVersion: VisiData 2.7"
  },
  {
    "path": "docs/customize.md",
    "chars": 5960,
    "preview": "---\neleventyNavigation:\n    key: Customizing VisiData\n    order: 12\nUpdated: 2023-11-18\nVersion: VisiData v3.0\n---\n\nFor "
  },
  {
    "path": "docs/dirsheet.md",
    "chars": 4297,
    "preview": "---\neleventyNavigation:\n  key: Directory Sheet\n  order: 99\n---\n\n# Directory Sheet (DirSheet)\n\nOpen any directory in Visi"
  },
  {
    "path": "docs/edit.md",
    "chars": 3672,
    "preview": "---\neleventyNavigation:\n  key: Editing Contents\n  order: 6\nUpdate: 2018-08-19\nVersion: VisiData 1.3.1\n---\n\n\n\n## How to e"
  },
  {
    "path": "docs/formats.md",
    "chars": 21964,
    "preview": "---\neleventyNavigation:\n    key: Supported Formats\n    order: 99\n---\n\n| filetype              | format                  "
  },
  {
    "path": "docs/freq.md",
    "chars": 4151,
    "preview": "---\neleventyNavigation:\n  key: Frequency Table Reference\n  order: 99\n---\n\n# Frequency Table Reference\n\nA frequency table"
  },
  {
    "path": "docs/gmail.md",
    "chars": 2703,
    "preview": "# Using Gmail with OAuth 2.0\n\n## Why\n\nAs of May 30 2022, Google [doesn't allow](https://support.google.com/accounts/answ"
  },
  {
    "path": "docs/graph.md",
    "chars": 2777,
    "preview": "---\neleventyNavigation:\n  key: Drawing graphs\n  order: 10\nUpdate: 2018-03-17\nVersion: VisiData 1.1\n---\n\n\n\nGraphs in Visi"
  },
  {
    "path": "docs/graphics.md",
    "chars": 9009,
    "preview": "---\neleventyNavigation:\n  key: Terminal Graphics in VisiData\n  order: 99\nUpdated: 2017-12-06\nVersion: VisiData 1.0\n---\n\n"
  },
  {
    "path": "docs/group.md",
    "chars": 5684,
    "preview": "---\neleventyNavigation:\n  key: Grouping data and descriptive statistics\n  order: 7\nUpdate: 2018-08-19\nVersion: VisiData "
  },
  {
    "path": "docs/index.md",
    "chars": 3279,
    "preview": "---\neleventyNavigation:\n    key: Getting Started\n    order: 1\n---\n\n## Getting started\n\n* [VisiData video demo](https://y"
  },
  {
    "path": "docs/internal_formats.md",
    "chars": 2064,
    "preview": "---\neleventyNavigation:\n    key: VisiData Internal Formats\n    order: 99\n---\n\nVisiData created these formats that it use"
  },
  {
    "path": "docs/join.md",
    "chars": 1918,
    "preview": "---\neleventyNavigation:\n  key: Combining datasets\n  order: 9\nUpdate: 2020-10-27\nVersion: VisiData 2.0.1\n---\n\n\n\n## How to"
  },
  {
    "path": "docs/loading.md",
    "chars": 3484,
    "preview": "---\neleventyNavigation:\n  key: Loading data\n  order: 2\n  parent: Getting Started\nupdate: 2018-12-18\nversion: VisiData 1."
  },
  {
    "path": "docs/macros.md",
    "chars": 1263,
    "preview": "---\neleventyNavigation:\n    key: Macros\n    order: 5\nupdate: 2023-11-18\nversion: VisiData 3.0\n---\n\n# Macros\n\nMacros allo"
  },
  {
    "path": "docs/menu.md",
    "chars": 2163,
    "preview": "---\neleventyNavigation:\n    key: Command Menus\n    order: 12\nUpdated: 2021-09-02\nVersion: VisiData v2.6\n---\n\nAt the top "
  },
  {
    "path": "docs/mouse.md",
    "chars": 409,
    "preview": "---\neleventyNavigation:\n    key: Mouse\n    order: 15\nUpdate: 2022-06-19\nVersion: VisiData v2.8\n---\n\n## How to disable th"
  },
  {
    "path": "docs/move.md",
    "chars": 795,
    "preview": "---\neleventyNavigation:\n  key: How can I move around and search?\n  order: 99\n---\n\n- basic movement just like vim\n  - 'G'"
  },
  {
    "path": "docs/navigate.md",
    "chars": 3412,
    "preview": "---\neleventyNavigation:\n  key: Navigation\n  order: 3\nupdated: 2020-07-18\nversion: VisiData 2.0\n---\n\n\n## How to rapidly s"
  },
  {
    "path": "docs/pipes.md",
    "chars": 1083,
    "preview": "---\neleventyNavigation:\n   key: Pipes\n   order: 14\nUpdate: 2019-09-11\nVersion: VisiData 2.0\n---\n\n\n## stdin/stdout pipe/r"
  },
  {
    "path": "docs/plugins.md",
    "chars": 2804,
    "preview": "---\neleventyNavigation:\n  key: Plugins\n  order: 13\nUpdated: 2023-11-22\nVersion: VisiData 3.0\n---\n\nA *plugin* is an optio"
  },
  {
    "path": "docs/rows.md",
    "chars": 7948,
    "preview": "---\neleventyNavigation:\n  key: Rows\n  order: 4\nUpdate: 2023-10-12\nVersion: VisiData 3.0\n---\n\n\n## [How to perform operati"
  },
  {
    "path": "docs/save-restore.md",
    "chars": 2687,
    "preview": "---\neleventyNavigation:\n    key: Save and Restore\n    order: 11\nUpdated: 2018-01-17\nVersion: VisiData 0.99\n---\n\n\n## How "
  },
  {
    "path": "docs/shell.md",
    "chars": 716,
    "preview": "---\neleventyNavigation:\n    key: Shell\n    order: 10\nupdate: 2022-08-16\nversion: VisiData v2.9\n---\n\n## How to add ZSH Co"
  },
  {
    "path": "docs/split.md",
    "chars": 2244,
    "preview": "---\neleventyNavigation:\n  key: Split view\n  order: 99\n---\n\n# Viewing two sheets simultaneously\n\nVisiData supports the vi"
  },
  {
    "path": "docs/test.md",
    "chars": 1369,
    "preview": "---\neleventyNavigation:\n    key: Contributing Tests\n    order: 99\nUpdate: 2024-01-10\nVersion: VisiData 3.0\n---\n\n\nThe `te"
  },
  {
    "path": "docs/usage.md",
    "chars": 1040,
    "preview": "---\neleventyNavigation:\n        key: Usage\n        order: 99\n---\n\n# How can I load files and exit the program?\n\nIf you u"
  },
  {
    "path": "docs/viewtsv.md",
    "chars": 2774,
    "preview": "---\neleventyNavigation:\n    key: viewtsv Annotated\n    order: 99\nDate: 2017-12-27\nVersion: 1.0\n---\n\n\n# viewtsv\n\n[viewtsv"
  },
  {
    "path": "platform/windows/vdwin-installer.md",
    "chars": 626,
    "preview": "# VisiData for Windows installer\n\n- install WinPY: https://winpython.github.io/\n\n- cd WinPY\\python-3.9.4.amd64\n- ./Scrip"
  },
  {
    "path": "platform/windows/vdwin.py",
    "chars": 67,
    "preview": "from visidata.main import vd_cli\nfrom visidata import vd\n\nvd_cli()\n"
  },
  {
    "path": "platform/windows/vdwin.spec",
    "chars": 1022,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\n\n\nblock_cipher = None\n\n\na = Analysis(['..\\\\vdwin\\\\vdwin.py'],\n             pathex"
  },
  {
    "path": "platform/www/Dockerfile",
    "chars": 1561,
    "preview": "FROM alpine:3.16\n\nRUN mkdir /app\nWORKDIR /app\n\nRUN mkdir -p /etc/vd \\\n    && apk update \\\n    && apk upgrade \\\n    && ap"
  },
  {
    "path": "platform/www/Makefile",
    "chars": 403,
    "preview": ".PHONY: run build push deploy\n\nrun: build\n\tdocker run -e PORT=9500 -p 9500:9500 --rm vdwww:latest\n\nbuild:\n\t# TODO(cpclou"
  },
  {
    "path": "platform/www/visidatarc",
    "chars": 1373,
    "preview": "def renamerizer(new_name, sheet):\n    sheet.names = (new_name,)\n    return sheet\n\n\nclass DataCatalog(IndexSheet):\n    ro"
  },
  {
    "path": "plugins/geocoding.py",
    "chars": 1581,
    "preview": "# uses API from nettoolkit.com\n\n'''Set options.ntk_key='<api-key>', then use command \"addcol-geocode\" on a column\nwith p"
  },
  {
    "path": "plugins/pcap/iana-ports.tsv",
    "chars": 215478,
    "preview": "transport\tport\tservice\ntcp\t1\ttcpmux\nudp\t1\ttcpmux\ntcp\t2\tcompressnet\nudp\t2\tcompressnet\ntcp\t3\tcompressnet\nudp\t3\tcompressnet"
  },
  {
    "path": "plugins/pcap/wireshark-oui.tsv",
    "chars": 1477486,
    "preview": "prefix\tshortname\towner\n00:00:00\t00:00:00\tOfficially Xerox, but 0:0:0:0:0:0 is more common\n00:00:01\tXerox\tXerox Corporati"
  },
  {
    "path": "plugins/vtask.py",
    "chars": 4225,
    "preview": "'Full terminal interface for TaskWarrior (task).'\n\nimport tasklib\nfrom visidata import vd, launchExternalEditorValue, Sh"
  },
  {
    "path": "pyrightconfig.json",
    "chars": 45,
    "preview": "{\n    \"reportAttributeAccessIssue\": \"none\"\n}\n"
  },
  {
    "path": "requirements.scm",
    "chars": 302,
    "preview": "(specifications->manifest\n '(\n   \"bash\"\n   \"coreutils\"\n   \"direnv\"\n   \"git\"\n   \"inetutils\"\n   \"nss-certs\"\n   \"python\"\n  "
  },
  {
    "path": "requirements.txt",
    "chars": 2026,
    "preview": "python-dateutil     # date type\n\n# optional dependencies\npandas>=1.5.3; python_version >= '3.11' # dta (Stata)\npandas>=1"
  },
  {
    "path": "ruff.toml",
    "chars": 1086,
    "preview": "# Ruff config for VisiData\n# Suppresses intentional style choices; flags real issues only.\n\n[lint]\npreview = true\nexplic"
  },
  {
    "path": "sample_data/1794685.fec",
    "chars": 1006,
    "preview": "HDR\u001cFEC\u001c8.4\u001cCampaign Manager 360\u001c1.0\u001cFEC-1724438\u001c6\u001c\nF2A\u001cS4MI00595\u001cRogers\u001cMichael\u001cJ\u001c\u001c\u001c\u001c\u001c\u001c\u001c\u001c\u001cPO Box 132\u001c\u001cSaint Joseph\u001cMI\u001c4"
  },
  {
    "path": "sample_data/StatusPR.csv",
    "chars": 26465,
    "preview": "Month,Day,Resource,Location,Value,Unit,Source\r\nSep,29,Electricity,Puerto Rico,5,percent,AEE\r\nSep,29,Telecomunications,Pu"
  },
  {
    "path": "sample_data/UTF-8-demo.txt",
    "chars": 7627,
    "preview": "\nUTF-8 encoded sample plain-text file\n‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n\nMarkus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.ca"
  },
  {
    "path": "sample_data/a.tsv",
    "chars": 33,
    "preview": "key\tsource_sheet\na_only\ta\nboth\ta\n"
  },
  {
    "path": "sample_data/b.tsv",
    "chars": 33,
    "preview": "key\tsource_sheet\nb_only\tb\nboth\tb\n"
  },
  {
    "path": "sample_data/benchmark.csv",
    "chars": 4593,
    "preview": "Date,Customer,SKU,Item,Quantity,Unit,Paid\r\n7/3/2018 1:47p,Robert Armstrong,FOOD213,BFF Oh My Gravy! Beef & Salmon 2.8oz,"
  },
  {
    "path": "sample_data/benchmark.fixed",
    "chars": 6708,
    "preview": "Date             Customer                   SKU     Item                                           Quantity Unit      Pa"
  },
  {
    "path": "sample_data/benchmark.jsonl",
    "chars": 9068,
    "preview": "# comment\n{\"Date\": \"7/3/2018 1:47p\", \"Customer\": [\"Robert Armstrong\", \"Dee Vasquez\"], \"SKU\": \"FOOD213\", \"Item\": \"BFF Oh "
  },
  {
    "path": "sample_data/benchmark.lsv",
    "chars": 6800,
    "preview": "Date: 7/3/2018 3:32p\nCustomer: Kyle Kennedy\nSKU: FOOD121\nItem: Food, Adult Cat - 3.5 oz\nQuantity: 1\nUnit: $4.22\nPaid: $4"
  },
  {
    "path": "sample_data/benchmark.psv",
    "chars": 4447,
    "preview": "Date|Customer|SKU|Item|Quantity|Unit|Paid\n7/3/2018 1:47p|Robert Armstrong|FOOD213|BFF Oh My Gravy! Beef & Salmon 2.8oz|4"
  },
  {
    "path": "sample_data/benchmark.xml",
    "chars": 13260,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<data>\n  <row>\n    <index>0</index>\n    <Date>7/3/2018 1:47p</Date>\n    <Customer"
  },
  {
    "path": "sample_data/benchmark.yml",
    "chars": 8092,
    "preview": "- customer: Robert Armstrong\n  date: 7/3/2018 1:47p\n  item: BFF Oh My Gravy! Beef & Salmon 2.8oz\n  paid: $51.8\n  quantit"
  },
  {
    "path": "sample_data/countries",
    "chars": 27528,
    "preview": "id     cc country-name-------------------------------- co keywords-------------------------------------------\r\n302672 AD"
  },
  {
    "path": "sample_data/empty-table.html",
    "chars": 36,
    "preview": "<table class=\"test_empty\">\n</table>\n"
  },
  {
    "path": "sample_data/freshwater-mammals.toml",
    "chars": 586,
    "preview": "# Adapted lovingly but hackily from:\n# https://blog.nature.org/2021/04/12/beaver-otter-muskrat-a-field-guide-to-freshwat"
  },
  {
    "path": "sample_data/gtm.f5log",
    "chars": 13530,
    "preview": "Oct 28 03:06:07 gtm1.site1.company.com err gtmd[18556]: 011ae0fa:3: iqmgmt_ssl_connect: SSL error: SSL connect (5)\nOct 2"
  },
  {
    "path": "sample_data/hello.mnu",
    "chars": 98,
    "preview": "x\ty\ttext\tcolor\tcommand\tinput\tcond\tstatus\n3\t4\thello world\t217 underline\tshow-status\tHello World!\t\t\n"
  },
  {
    "path": "sample_data/issue1308.html",
    "chars": 460,
    "preview": "<table >\n        <tbody>\n                <tr>\n                        <td rowspan=3>1.1 </td>\n                        <t"
  },
  {
    "path": "sample_data/numeric-cols.tsv",
    "chars": 2035,
    "preview": "5\tRegion\t\tItem\tUnits\tUnit_Cost\tTotal\n2016-01-06\tEast\tJones\tPencil\t95\t1.99\t189.05\n2016-01-23\tCentral\tKivell\tBinder\t50\t19."
  },
  {
    "path": "sample_data/officials.jsonla",
    "chars": 141,
    "preview": "[\"Name\", \"Number\", \"Exile\"]\n[\"Daniel\", 1, true]\n[\"Hananiah\", 2, true]\n[\"Mishael\", 3, true]\n[\"Azariah\", 4, true]\n[\"Nebuch"
  },
  {
    "path": "sample_data/pr2815.jsonl",
    "chars": 1596,
    "preview": "[\"description\", \"col1\", \"col2\"]\n[\"normal ASCII cell looks fine:\", \"ordinary contents\", \"\"]\n[\"but cell ending in ASCII ch"
  },
  {
    "path": "sample_data/sample.conllu",
    "chars": 6192,
    "preview": "# sent_id = dev-s1\n# text = ただし、50周年ソングに変更後は、EDも歌つきのものが使われた。\n1\tただし\tただし\tCCONJ\t接続詞\t_\t18\tcc\t_\tBunsetuPositionType=SEM_HEAD|"
  },
  {
    "path": "sample_data/sample.geojson",
    "chars": 6927,
    "preview": "{\"type\": \"FeatureCollection\", \"features\": [{\"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[-117.215796, 32.72734], [-"
  },
  {
    "path": "sample_data/sample.tsv",
    "chars": 2046,
    "preview": "OrderDate\tRegion\tRep\tItem\tUnits\tUnit_Cost\tTotal\n2016-01-06\tEast\tJones\tPencil\t95\t1.99\t189.05\n2016-01-23\tCentral\tKivell\tBi"
  },
  {
    "path": "sample_data/sample.vds",
    "chars": 16704,
    "preview": "#{\"name\": \"sample\"}\n#{\"name\": \"OrderDate\", \"width\": 18, \"height\": 1, \"expr\": \"OrderDate\", \"keycol\": 0, \"fmtstr\": \"%Y-%m-"
  },
  {
    "path": "sample_data/saulpw-008.xd",
    "chars": 4004,
    "preview": "Title: \"How could you know anything of the matter?\"\nAuthor: Saul Pwanson\nDate: 2017-02-28\n\n\nBEG#PASSED#MOAT\nEDO#ALLURE#E"
  },
  {
    "path": "sample_data/shapefile/WA_State_Boundary.cpg",
    "chars": 5,
    "preview": "UTF-8"
  },
  {
    "path": "sample_data/shapefile/WA_State_Boundary.prj",
    "chars": 143,
    "preview": "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\""
  },
  {
    "path": "sample_data/shapefile/WA_State_Boundary.xml",
    "chars": 1198,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><metadata>\n<idinfo>\n<citation>\n<citeinfo>\n<title>WA State Boundary</title>\n<onlink"
  },
  {
    "path": "sample_data/smiths-json.grep",
    "chars": 7407,
    "preview": "{\"type\":\"begin\",\"data\":{\"path\":{\"text\":\"benchmark.csv\"}}}\n{\"type\":\"match\",\"data\":{\"path\":{\"text\":\"benchmark.csv\"},\"lines"
  },
  {
    "path": "sample_data/smiths-standard.grep",
    "chars": 1541,
    "preview": "benchmark.csv:36:8/16/2018 5:15p,Michael Smith,BIRD160,\"Parakeet, Blue (Melopsittacus undulatus)\",1,29.95,$31.85\r\nbenchm"
  },
  {
    "path": "sample_data/states.yml",
    "chars": 47517,
    "preview": "---\n  - \n    state: \"Alabama\"\n    slug: \"alabama\"\n    code: \"AL\"\n    nickname: \"Yellowhammer State\"\n    website: \"http:/"
  },
  {
    "path": "sample_data/sunshinelist.html",
    "chars": 33287,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<title>Sunshine List - Historical Public Disclosure Data for Ontario</title>\n"
  },
  {
    "path": "sample_data/test-fixed-leadingspaces.txt",
    "chars": 36,
    "preview": "  1 aaa bbb\n 20 ccc ddd\n300 eee fff\n"
  },
  {
    "path": "sample_data/test-unicode-display.tsv",
    "chars": 353,
    "preview": "test\tdata\tresult\nascii\tHong Kong!\tHong Kong!\nbmp halfwidth\t❤\theart\nbmp fullwidth\t香港\t2 kanji\nright-to-left\t‏پاکستانی\tarab"
  },
  {
    "path": "sample_data/test.fixed",
    "chars": 68,
    "preview": "    a   b   c\n    d    e    f\n1   ggg hhh iiii\n 2  jjj kkk llllllll\n"
  }
]

// ... and 795 more files (download for full content)

About this extraction

This page contains the full source code of the saulpw/visidata GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 995 files (20.4 MB), approximately 1.7M tokens, and a symbol index with 2918 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!