We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[tsv] add CLI option to use NUL as delimiter
It's useful to parse output from GNU grep's `-Z` option. That produces lines that in Python are `f'{filename}\0{line}\n'`, instead of the usual `f'{filename}:{line}\n'`.

Right now the command line can't be used to specify a NUL delimiter, as in `vd --delimiter="\0"`, because `sys.argv` strings are NUL-terminated and can't ever contain NUL.

My workarounds for now are to use .visidatarc, either add a temporary line:
`vd.option('delimiter', '\x00', 'field delimiter to use for tsv/usv filetype', replay=True)`.
or add a new filetype to allow `vd -f nsv`:
```
@VisiData.api
def open_nsv(vd, p):
    tsv = TsvSheet(p.base_stem, source=p)
    tsv.delimiter = '\x00'
    tsv.reload()
    return tsv
```
Can `open_nsv()` be written without `reload()` right now? I couldn't think of another way to set `delimiter` for TsvSheet.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `visidata/loaders/tsv.py`
Content:
```
1 import os
2 import contextlib
3 import itertools
4 import collections
5 import math
6 import time
7 
8 from visidata import vd, asyncthread, options, Progress, ColumnItem, SequenceSheet, Sheet, VisiData
9 from visidata import namedlist, filesize
10 
11 vd.option('delimiter', '\t', 'field delimiter to use for tsv/usv filetype', replay=True)
12 vd.option('row_delimiter', '\n', 'row delimiter to use for tsv/usv filetype', replay=True)
13 vd.option('tsv_safe_newline', '\u001e', 'replacement for newline character when saving to tsv', replay=True)
14 vd.option('tsv_safe_tab', '\u001f', 'replacement for tab character when saving to tsv', replay=True)
15 
16 
17 @VisiData.api
18 def open_tsv(vd, p):
19     return TsvSheet(p.base_stem, source=p)
20 
21 
22 def adaptive_bufferer(fp, max_buffer_size=65536):
23     """Loading e.g. tsv files goes faster with a large buffer. But when the input stream
24     is slow (e.g. 1 byte/second) and the buffer size is large, it can take a long time until
25     the buffer is filled. Only when the buffer is filled (or the input stream is finished)
26     you can see the data visiualized in visidata. That's why we use an adaptive buffer.
27     For fast input streams, the buffer becomes large, for slow input streams, the buffer stays
28     small"""
29     buffer_size = 8
30     processed_buffer_size = 0
31     previous_start_time = time.time()
32     while True:
33         next_chunk = fp.read(max(buffer_size, 1))
34         if not next_chunk:
35             break
36 
37         yield next_chunk
38 
39         processed_buffer_size += len(next_chunk)
40 
41         current_time = time.time()
42         current_delta = current_time - previous_start_time
43 
44         if current_delta < 1:
45             # if it takes longer than one second to fill the buffer, double the size of the buffer
46             buffer_size = min(buffer_size * 2, max_buffer_size)
47         else:
48             # if it takes less than one second, increase the buffer size so it takes about
49             # 1 second to fill it
50             previous_start_time = current_time
51             buffer_size = math.ceil(min(processed_buffer_size / current_delta, max_buffer_size))
52             processed_buffer_size = 0
53 
54 def splitter(stream, delim='\n'):
55     'Generates one line/row/record at a time from stream, separated by delim'
56 
57     buf = type(delim)()
58 
59     for chunk in stream:
60         buf += chunk
61 
62         *rows, buf = buf.split(delim)
63         yield from rows
64 
65     buf = buf.rstrip(delim)  # trim empty trailing lines
66     if buf:
67         yield from buf.rstrip(delim).split(delim)
68 
69 
70 # rowdef: list
71 class TsvSheet(SequenceSheet):
72     delimiter = ''
73     row_delimiter = ''
74 
75     def iterload(self):
76         delim = self.delimiter or self.options.delimiter
77         rowdelim = self.row_delimiter or self.options.row_delimiter
78 
79         with self.open_text_source() as fp:
80                 for line in splitter(adaptive_bufferer(fp), rowdelim):
81                     if not line or fp._regex_skip.match(line):
82                         continue
83 
84                     row = list(line.split(delim))
85 
86                     if len(row) < self.nVisibleCols:
87                         # extend rows that are missing entries
88                         row.extend([None]*(self.nVisibleCols-len(row)))
89 
90                     yield row
91 
92 
93 @VisiData.api
94 def save_tsv(vd, p, vs, delimiter='', row_delimiter=''):
95     'Write sheet to file `fn` as TSV.'
96     unitsep = delimiter or vs.options.delimiter
97     rowsep = row_delimiter or vs.options.row_delimiter
98     trdict = vs.safe_trdict()
99 
100     with p.open(mode='w', encoding=vs.options.save_encoding) as fp:
101         colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + rowsep
102         fp.write(colhdr)
103 
104         for dispvals in vs.iterdispvals(format=True):
105             fp.write(unitsep.join(dispvals.values()))
106             fp.write(rowsep)
107 
108 
109 @Sheet.api
110 def append_tsv_row(vs, row):
111     'Append `row` to vs.source, creating file with correct headers if necessary. For internal use only.'
112     if not vs.source.exists():
113         with contextlib.suppress(FileExistsError):
114             parentdir = vs.source.parent
115             if parentdir:
116                 os.makedirs(parentdir)
117 
118         # Write tsv header for Sheet `vs` to Path `p`
119         trdict = vs.safe_trdict()
120         unitsep = options.delimiter
121 
122         with vs.source.open(mode='w') as fp:
123             colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + vs.options.row_delimiter
124             if colhdr.strip():  # is anything but whitespace
125                 fp.write(colhdr)
126 
127     newrow = ''
128 
129     contents = vs.source.open(mode='r').read()
130     if not contents.endswith('\n'):  #1569
131         newrow += '\n'
132 
133     newrow += '\t'.join(col.getDisplayValue(row) for col in vs.visibleCols) + '\n'
134 
135     with vs.source.open(mode='a') as fp:
136         fp.write(newrow)
137 
138 
139 TsvSheet.options.regex_skip = '^#.*'
140 
141 vd.addGlobals({
142     'TsvSheet': TsvSheet,
143 })
144
```

Path: `visidata/features/xsv_guide.py`
Content:
```
1 from visidata import GuideSheet, vd
2 
3 
4 class XsvGuide(GuideSheet):
5     guide_text = '''# CSV/TSV options
6 
7     ## `tsv` (Tab Separated Values), as simple as it gets
8 
9     - {help.options.delimiter}
10     - {help.options.row_delimiter}
11     - {help.options.tsv_safe_newline}
12     - {help.options.tsv_safe_tab}
13 
14     Use `-f usv` for Unicode separators U+241F and U+241E.
15     Use `-f lsv` for awk-like records.
16 
17     ## `csv` (Comma Separated Values) for maximum computibility
18 
19     .csv files are a scourge upon the earth, and still regrettably common.
20     All csv_* options are passed unchanged into csv.reader() and csv.writer().
21 
22     - {help.options.csv_dialect}
23        - Accepted dialects are `excel-tab`, `unix`, and `excel`.
24     - {help.options.csv_delimiter}
25     - {help.options.csv_quotechar}
26     - {help.options.csv_skipinitialspace}
27     - {help.options.csv_escapechar}
28     - {help.options.csv_lineterminator}
29 
30     ## Saving TSV/CSV files
31 
32     - {help.options.save_filetype}
33     - {help.options.safety_first}
34 
35     ## Useful options for text formats in general
36 
37     - {help.options.regex_skip}
38     - {help.options.save_encoding}
39     '''
40 
41 
42 vd.addGuide('XsvGuide', XsvGuide)
43
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/visidata/features/xsv_guide.py b/visidata/features/xsv_guide.py
--- a/visidata/features/xsv_guide.py
+++ b/visidata/features/xsv_guide.py
@@ -13,6 +13,8 @@
 
     Use `-f usv` for Unicode separators U+241F and U+241E.
     Use `-f lsv` for awk-like records.
+    Use `--delimiter=` (an empty string) to make '\0' the value separator.
+    Use `--row-delimiter=` to make '\0' the row separator.
 
     ## `csv` (Comma Separated Values) for maximum computibility
 
diff --git a/visidata/loaders/tsv.py b/visidata/loaders/tsv.py
--- a/visidata/loaders/tsv.py
+++ b/visidata/loaders/tsv.py
@@ -75,6 +75,14 @@
     def iterload(self):
         delim = self.delimiter or self.options.delimiter
         rowdelim = self.row_delimiter or self.options.row_delimiter
+        if delim == '':
+            vd.warning("using '\\x00' as field delimiter")
+            delim = '\x00'  #2272
+        if rowdelim == '':
+            vd.warning("using '\\x00' as row delimiter")
+            rowdelim = '\x00'
+        if delim == rowdelim:
+            vd.fail('field delimiter and row delimiter cannot be the same')
 
         with self.open_text_source() as fp:
                 for line in splitter(adaptive_bufferer(fp), rowdelim):
@@ -95,6 +103,14 @@
     'Write sheet to file `fn` as TSV.'
     unitsep = delimiter or vs.options.delimiter
     rowsep = row_delimiter or vs.options.row_delimiter
+    if unitsep == '':
+        vd.warning("saving with '\\x00' as field delimiter")
+        unitsep = '\x00'
+    if rowsep == '':
+        vd.warning("saving with '\\x00' as row delimiter")
+        rowsep = '\x00'
+    if unitsep == rowsep:
+        vd.fail('field delimiter and row delimiter cannot be the same')
     trdict = vs.safe_trdict()
 
     with p.open(mode='w', encoding=vs.options.save_encoding) as fp:

{"golden_diff": "diff --git a/visidata/features/xsv_guide.py b/visidata/features/xsv_guide.py\n--- a/visidata/features/xsv_guide.py\n+++ b/visidata/features/xsv_guide.py\n@@ -13,6 +13,8 @@\n \n     Use `-f usv` for Unicode separators U+241F and U+241E.\n     Use `-f lsv` for awk-like records.\n+    Use `--delimiter=` (an empty string) to make '\\0' the value separator.\n+    Use `--row-delimiter=` to make '\\0' the row separator.\n \n     ## `csv` (Comma Separated Values) for maximum computibility\n \ndiff --git a/visidata/loaders/tsv.py b/visidata/loaders/tsv.py\n--- a/visidata/loaders/tsv.py\n+++ b/visidata/loaders/tsv.py\n@@ -75,6 +75,14 @@\n     def iterload(self):\n         delim = self.delimiter or self.options.delimiter\n         rowdelim = self.row_delimiter or self.options.row_delimiter\n+        if delim == '':\n+            vd.warning(\"using '\\\\x00' as field delimiter\")\n+            delim = '\\x00'  #2272\n+        if rowdelim == '':\n+            vd.warning(\"using '\\\\x00' as row delimiter\")\n+            rowdelim = '\\x00'\n+        if delim == rowdelim:\n+            vd.fail('field delimiter and row delimiter cannot be the same')\n \n         with self.open_text_source() as fp:\n                 for line in splitter(adaptive_bufferer(fp), rowdelim):\n@@ -95,6 +103,14 @@\n     'Write sheet to file `fn` as TSV.'\n     unitsep = delimiter or vs.options.delimiter\n     rowsep = row_delimiter or vs.options.row_delimiter\n+    if unitsep == '':\n+        vd.warning(\"saving with '\\\\x00' as field delimiter\")\n+        unitsep = '\\x00'\n+    if rowsep == '':\n+        vd.warning(\"saving with '\\\\x00' as row delimiter\")\n+        rowsep = '\\x00'\n+    if unitsep == rowsep:\n+        vd.fail('field delimiter and row delimiter cannot be the same')\n     trdict = vs.safe_trdict()\n \n     with p.open(mode='w', encoding=vs.options.save_encoding) as fp:\n", "issue": "[tsv] add CLI option to use NUL as delimiter\nIt's useful to parse output from GNU grep's `-Z` option. That produces lines that in Python are `f'{filename}\\0{line}\\n'`, instead of the usual `f'{filename}:{line}\\n'`.\r\n\r\nRight now the command line can't be used to specify a NUL delimiter, as in `vd --delimiter=\"\\0\"`, because `sys.argv` strings are NUL-terminated and can't ever contain NUL.\r\n\r\nMy workarounds for now are to use .visidatarc, either add a temporary line:\r\n`vd.option('delimiter', '\\x00', 'field delimiter to use for tsv/usv filetype', replay=True)`.\r\nor add a new filetype to allow `vd -f nsv`:\r\n```\r\n@VisiData.api\r\ndef open_nsv(vd, p):\r\n    tsv = TsvSheet(p.base_stem, source=p)\r\n    tsv.delimiter = '\\x00'\r\n    tsv.reload()\r\n    return tsv\r\n```\r\nCan `open_nsv()` be written without `reload()` right now? I couldn't think of another way to set `delimiter` for TsvSheet.\n", "before_files": [{"content": "import os\nimport contextlib\nimport itertools\nimport collections\nimport math\nimport time\n\nfrom visidata import vd, asyncthread, options, Progress, ColumnItem, SequenceSheet, Sheet, VisiData\nfrom visidata import namedlist, filesize\n\nvd.option('delimiter', '\\t', 'field delimiter to use for tsv/usv filetype', replay=True)\nvd.option('row_delimiter', '\\n', 'row delimiter to use for tsv/usv filetype', replay=True)\nvd.option('tsv_safe_newline', '\\u001e', 'replacement for newline character when saving to tsv', replay=True)\nvd.option('tsv_safe_tab', '\\u001f', 'replacement for tab character when saving to tsv', replay=True)\n\n\n@VisiData.api\ndef open_tsv(vd, p):\n    return TsvSheet(p.base_stem, source=p)\n\n\ndef adaptive_bufferer(fp, max_buffer_size=65536):\n    \"\"\"Loading e.g. tsv files goes faster with a large buffer. But when the input stream\n    is slow (e.g. 1 byte/second) and the buffer size is large, it can take a long time until\n    the buffer is filled. Only when the buffer is filled (or the input stream is finished)\n    you can see the data visiualized in visidata. That's why we use an adaptive buffer.\n    For fast input streams, the buffer becomes large, for slow input streams, the buffer stays\n    small\"\"\"\n    buffer_size = 8\n    processed_buffer_size = 0\n    previous_start_time = time.time()\n    while True:\n        next_chunk = fp.read(max(buffer_size, 1))\n        if not next_chunk:\n            break\n\n        yield next_chunk\n\n        processed_buffer_size += len(next_chunk)\n\n        current_time = time.time()\n        current_delta = current_time - previous_start_time\n\n        if current_delta < 1:\n            # if it takes longer than one second to fill the buffer, double the size of the buffer\n            buffer_size = min(buffer_size * 2, max_buffer_size)\n        else:\n            # if it takes less than one second, increase the buffer size so it takes about\n            # 1 second to fill it\n            previous_start_time = current_time\n            buffer_size = math.ceil(min(processed_buffer_size / current_delta, max_buffer_size))\n            processed_buffer_size = 0\n\ndef splitter(stream, delim='\\n'):\n    'Generates one line/row/record at a time from stream, separated by delim'\n\n    buf = type(delim)()\n\n    for chunk in stream:\n        buf += chunk\n\n        *rows, buf = buf.split(delim)\n        yield from rows\n\n    buf = buf.rstrip(delim)  # trim empty trailing lines\n    if buf:\n        yield from buf.rstrip(delim).split(delim)\n\n\n# rowdef: list\nclass TsvSheet(SequenceSheet):\n    delimiter = ''\n    row_delimiter = ''\n\n    def iterload(self):\n        delim = self.delimiter or self.options.delimiter\n        rowdelim = self.row_delimiter or self.options.row_delimiter\n\n        with self.open_text_source() as fp:\n                for line in splitter(adaptive_bufferer(fp), rowdelim):\n                    if not line or fp._regex_skip.match(line):\n                        continue\n\n                    row = list(line.split(delim))\n\n                    if len(row) < self.nVisibleCols:\n                        # extend rows that are missing entries\n                        row.extend([None]*(self.nVisibleCols-len(row)))\n\n                    yield row\n\n\n@VisiData.api\ndef save_tsv(vd, p, vs, delimiter='', row_delimiter=''):\n    'Write sheet to file `fn` as TSV.'\n    unitsep = delimiter or vs.options.delimiter\n    rowsep = row_delimiter or vs.options.row_delimiter\n    trdict = vs.safe_trdict()\n\n    with p.open(mode='w', encoding=vs.options.save_encoding) as fp:\n        colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + rowsep\n        fp.write(colhdr)\n\n        for dispvals in vs.iterdispvals(format=True):\n            fp.write(unitsep.join(dispvals.values()))\n            fp.write(rowsep)\n\n\n@Sheet.api\ndef append_tsv_row(vs, row):\n    'Append `row` to vs.source, creating file with correct headers if necessary. For internal use only.'\n    if not vs.source.exists():\n        with contextlib.suppress(FileExistsError):\n            parentdir = vs.source.parent\n            if parentdir:\n                os.makedirs(parentdir)\n\n        # Write tsv header for Sheet `vs` to Path `p`\n        trdict = vs.safe_trdict()\n        unitsep = options.delimiter\n\n        with vs.source.open(mode='w') as fp:\n            colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + vs.options.row_delimiter\n            if colhdr.strip():  # is anything but whitespace\n                fp.write(colhdr)\n\n    newrow = ''\n\n    contents = vs.source.open(mode='r').read()\n    if not contents.endswith('\\n'):  #1569\n        newrow += '\\n'\n\n    newrow += '\\t'.join(col.getDisplayValue(row) for col in vs.visibleCols) + '\\n'\n\n    with vs.source.open(mode='a') as fp:\n        fp.write(newrow)\n\n\nTsvSheet.options.regex_skip = '^#.*'\n\nvd.addGlobals({\n    'TsvSheet': TsvSheet,\n})\n", "path": "visidata/loaders/tsv.py"}, {"content": "from visidata import GuideSheet, vd\n\n\nclass XsvGuide(GuideSheet):\n    guide_text = '''# CSV/TSV options\n\n    ## `tsv` (Tab Separated Values), as simple as it gets\n\n    - {help.options.delimiter}\n    - {help.options.row_delimiter}\n    - {help.options.tsv_safe_newline}\n    - {help.options.tsv_safe_tab}\n\n    Use `-f usv` for Unicode separators U+241F and U+241E.\n    Use `-f lsv` for awk-like records.\n\n    ## `csv` (Comma Separated Values) for maximum computibility\n\n    .csv files are a scourge upon the earth, and still regrettably common.\n    All csv_* options are passed unchanged into csv.reader() and csv.writer().\n\n    - {help.options.csv_dialect}\n       - Accepted dialects are `excel-tab`, `unix`, and `excel`.\n    - {help.options.csv_delimiter}\n    - {help.options.csv_quotechar}\n    - {help.options.csv_skipinitialspace}\n    - {help.options.csv_escapechar}\n    - {help.options.csv_lineterminator}\n\n    ## Saving TSV/CSV files\n\n    - {help.options.save_filetype}\n    - {help.options.safety_first}\n\n    ## Useful options for text formats in general\n\n    - {help.options.regex_skip}\n    - {help.options.save_encoding}\n    '''\n\n\nvd.addGuide('XsvGuide', XsvGuide)\n", "path": "visidata/features/xsv_guide.py"}], "after_files": [{"content": "import os\nimport contextlib\nimport itertools\nimport collections\nimport math\nimport time\n\nfrom visidata import vd, asyncthread, options, Progress, ColumnItem, SequenceSheet, Sheet, VisiData\nfrom visidata import namedlist, filesize\n\nvd.option('delimiter', '\\t', 'field delimiter to use for tsv/usv filetype', replay=True)\nvd.option('row_delimiter', '\\n', 'row delimiter to use for tsv/usv filetype', replay=True)\nvd.option('tsv_safe_newline', '\\u001e', 'replacement for newline character when saving to tsv', replay=True)\nvd.option('tsv_safe_tab', '\\u001f', 'replacement for tab character when saving to tsv', replay=True)\n\n\n@VisiData.api\ndef open_tsv(vd, p):\n    return TsvSheet(p.base_stem, source=p)\n\n\ndef adaptive_bufferer(fp, max_buffer_size=65536):\n    \"\"\"Loading e.g. tsv files goes faster with a large buffer. But when the input stream\n    is slow (e.g. 1 byte/second) and the buffer size is large, it can take a long time until\n    the buffer is filled. Only when the buffer is filled (or the input stream is finished)\n    you can see the data visiualized in visidata. That's why we use an adaptive buffer.\n    For fast input streams, the buffer becomes large, for slow input streams, the buffer stays\n    small\"\"\"\n    buffer_size = 8\n    processed_buffer_size = 0\n    previous_start_time = time.time()\n    while True:\n        next_chunk = fp.read(max(buffer_size, 1))\n        if not next_chunk:\n            break\n\n        yield next_chunk\n\n        processed_buffer_size += len(next_chunk)\n\n        current_time = time.time()\n        current_delta = current_time - previous_start_time\n\n        if current_delta < 1:\n            # if it takes longer than one second to fill the buffer, double the size of the buffer\n            buffer_size = min(buffer_size * 2, max_buffer_size)\n        else:\n            # if it takes less than one second, increase the buffer size so it takes about\n            # 1 second to fill it\n            previous_start_time = current_time\n            buffer_size = math.ceil(min(processed_buffer_size / current_delta, max_buffer_size))\n            processed_buffer_size = 0\n\ndef splitter(stream, delim='\\n'):\n    'Generates one line/row/record at a time from stream, separated by delim'\n\n    buf = type(delim)()\n\n    for chunk in stream:\n        buf += chunk\n\n        *rows, buf = buf.split(delim)\n        yield from rows\n\n    buf = buf.rstrip(delim)  # trim empty trailing lines\n    if buf:\n        yield from buf.rstrip(delim).split(delim)\n\n\n# rowdef: list\nclass TsvSheet(SequenceSheet):\n    delimiter = ''\n    row_delimiter = ''\n\n    def iterload(self):\n        delim = self.delimiter or self.options.delimiter\n        rowdelim = self.row_delimiter or self.options.row_delimiter\n        if delim == '':\n            vd.warning(\"using '\\\\x00' as field delimiter\")\n            delim = '\\x00'  #2272\n        if rowdelim == '':\n            vd.warning(\"using '\\\\x00' as row delimiter\")\n            rowdelim = '\\x00'\n        if delim == rowdelim:\n            vd.fail('field delimiter and row delimiter cannot be the same')\n\n        with self.open_text_source() as fp:\n                for line in splitter(adaptive_bufferer(fp), rowdelim):\n                    if not line or fp._regex_skip.match(line):\n                        continue\n\n                    row = list(line.split(delim))\n\n                    if len(row) < self.nVisibleCols:\n                        # extend rows that are missing entries\n                        row.extend([None]*(self.nVisibleCols-len(row)))\n\n                    yield row\n\n\n@VisiData.api\ndef save_tsv(vd, p, vs, delimiter='', row_delimiter=''):\n    'Write sheet to file `fn` as TSV.'\n    unitsep = delimiter or vs.options.delimiter\n    rowsep = row_delimiter or vs.options.row_delimiter\n    if unitsep == '':\n        vd.warning(\"saving with '\\\\x00' as field delimiter\")\n        unitsep = '\\x00'\n    if rowsep == '':\n        vd.warning(\"saving with '\\\\x00' as row delimiter\")\n        rowsep = '\\x00'\n    if unitsep == rowsep:\n        vd.fail('field delimiter and row delimiter cannot be the same')\n    trdict = vs.safe_trdict()\n\n    with p.open(mode='w', encoding=vs.options.save_encoding) as fp:\n        colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + rowsep\n        fp.write(colhdr)\n\n        for dispvals in vs.iterdispvals(format=True):\n            fp.write(unitsep.join(dispvals.values()))\n            fp.write(rowsep)\n\n\n@Sheet.api\ndef append_tsv_row(vs, row):\n    'Append `row` to vs.source, creating file with correct headers if necessary. For internal use only.'\n    if not vs.source.exists():\n        with contextlib.suppress(FileExistsError):\n            parentdir = vs.source.parent\n            if parentdir:\n                os.makedirs(parentdir)\n\n        # Write tsv header for Sheet `vs` to Path `p`\n        trdict = vs.safe_trdict()\n        unitsep = options.delimiter\n\n        with vs.source.open(mode='w') as fp:\n            colhdr = unitsep.join(col.name.translate(trdict) for col in vs.visibleCols) + vs.options.row_delimiter\n            if colhdr.strip():  # is anything but whitespace\n                fp.write(colhdr)\n\n    newrow = ''\n\n    contents = vs.source.open(mode='r').read()\n    if not contents.endswith('\\n'):  #1569\n        newrow += '\\n'\n\n    newrow += '\\t'.join(col.getDisplayValue(row) for col in vs.visibleCols) + '\\n'\n\n    with vs.source.open(mode='a') as fp:\n        fp.write(newrow)\n\n\nTsvSheet.options.regex_skip = '^#.*'\n\nvd.addGlobals({\n    'TsvSheet': TsvSheet,\n})\n", "path": "visidata/loaders/tsv.py"}, {"content": "from visidata import GuideSheet, vd\n\n\nclass XsvGuide(GuideSheet):\n    guide_text = '''# CSV/TSV options\n\n    ## `tsv` (Tab Separated Values), as simple as it gets\n\n    - {help.options.delimiter}\n    - {help.options.row_delimiter}\n    - {help.options.tsv_safe_newline}\n    - {help.options.tsv_safe_tab}\n\n    Use `-f usv` for Unicode separators U+241F and U+241E.\n    Use `-f lsv` for awk-like records.\n    Use `--delimiter=` (an empty string) to make '\\0' the value separator.\n    Use `--row-delimiter=` to make '\\0' the row separator.\n\n    ## `csv` (Comma Separated Values) for maximum computibility\n\n    .csv files are a scourge upon the earth, and still regrettably common.\n    All csv_* options are passed unchanged into csv.reader() and csv.writer().\n\n    - {help.options.csv_dialect}\n       - Accepted dialects are `excel-tab`, `unix`, and `excel`.\n    - {help.options.csv_delimiter}\n    - {help.options.csv_quotechar}\n    - {help.options.csv_skipinitialspace}\n    - {help.options.csv_escapechar}\n    - {help.options.csv_lineterminator}\n\n    ## Saving TSV/CSV files\n\n    - {help.options.save_filetype}\n    - {help.options.safety_first}\n\n    ## Useful options for text formats in general\n\n    - {help.options.regex_skip}\n    - {help.options.save_encoding}\n    '''\n\n\nvd.addGuide('XsvGuide', XsvGuide)\n", "path": "visidata/features/xsv_guide.py"}]}