Tabulify - View delimited and JSON files as tables
A Visual Studio Code extension that opens delimited text files such as CSV and TSV as a real table and lets you edit them, and opens JSON and JSON Lines files as tables to read. Tabulify indexes a file progressively so rows appear while it is still reading, renders only the rows on screen so a file far larger than memory scrolls at frame rate, and rewrites only the rows you actually edited — every other byte is copied through untouched.
Features
Clear Data Presentation
- View files with delimiter (such as CSV) in a clean, formatted table with proper column alignment
- Distinct color coding for each column to improve readability
- Automatic theme integration (supports both light and dark VS Code themes)
- Support for files with or without headers
- Support for various delimiters including escaped delimiters such as tab (\t), new line (\n, \r, \r\n), backspace (\b), form-feed (\f), vertical tab (\v), null character (\0), backslash (\)
Large-file Table View
- Rows appear while the file is still being indexed
- A virtual grid recycles the rows around the viewport instead of rendering the whole file
- Files above the configurable memory threshold are read from disk on demand
- Ragged and malformed rows are reported rather than silently repaired
Flexible Column Management
- Automatic column width calculation based on content
- Resizable columns by dragging column dividers
- Fit To Screen support
- Fixed-height, single-line cells keep scrolling predictable at any supported file size; full values are available in the Inspector and by expanding a row
Smart Content Handling
- Robust file parsing that handles:
- Quoted and unquoted fields
- Delimiters and newlines within quoted fields
- Escaped quotes
- Leading and trailing whitespace, preserved exactly rather than trimmed
- Rows with varying field counts, reported rather than padded away
- Correct quoting on copy, so a cell containing the delimiter survives a paste-and-parse round trip
JSON and JSON Lines
- Open
.json, .jsonl, and .ndjson files as tables without converting them first
- A JSON object becomes one row whose property names are the columns; a list of objects becomes one row per entry, with the columns being the union of their property names
- Lists of lists become numbered columns, and lists of plain values become a single
Value column
- Read progressively on the same index as a delimited file, with no size limit beyond the 4 GiB and 32-million-row ceilings every file has
- Numbers keep their exact text, so
1e400, 1.0, and a twenty-digit integer all survive unchanged
- Nested objects and lists are one cell with a compact preview; the Inspector shows the whole value pretty-printed
- A record that cannot be read is marked rather than repaired or dropped, and its original text stays available in the Raw tab
- Find, Filter, Go-To, selection, the Inspector, and copy all work the same way they do on a delimited file
- Read-only in this release: see Current Limitations
Inspecting Values
- A collapsible Inspector panel shows the complete value of the active cell, including whitespace and newlines inside quoted fields
- A Raw tab shows the file's own bytes for the row under the cursor, with quoting and terminators as the file stores them
Enter expands the focused row over the grid, listing every column's full value
- Expansion never changes the scrollbar, at any file size, because it is drawn over the grid rather than inside it
- The Raw line is read on demand, so a closed panel costs nothing and holding an arrow key does not read every row
Finding Values
Ctrl/Cmd+F searches the whole file, not just the rows on screen, while the panel stays responsive
- The match count climbs live and says whether it is still searching, so a count is never presented as final before it is
- Smart case: a lower-case query ignores case, a capital makes the search exact;
Aa forces exact matching
- Regular expressions with
.*, matched against one field value at a time; an invalid pattern is reported without echoing it back
- Restrict a search to a single column by its header
Enter and Shift+Enter step through matches, wrapping at both ends, moving the cursor onto the matching cell and scrolling by the smallest amount that shows it
- Every visible occurrence is highlighted in place, with the current match distinguished from the rest
- A search started before indexing finishes keeps counting as more rows arrive
Filtering Rows
- One filter box per column, plus one that matches any column; terms combine so every one must match
- Only the matching rows are shown, and the rest of the panel keeps working while the file is scanned
- Matching rows appear within milliseconds on a file of any size, with the count climbing until the scan settles
- Terms are literal substrings, case-insensitive unless they contain a capital, with leading and trailing spaces treated as part of the term
- Find, copy, Go-To, and the Inspector all operate on what is displayed while a filter is active
- Filtering never writes to the file, and closing the filter bar always clears it
Jumping to a Row or Column
Ctrl/Cmd+G opens VS Code's own quick-pick over the grid: a number jumps to that row, anything else finds a column by name
- The number you type is the number in the row-number gutter, so it always agrees with the status bar
- Column matching is VS Code's own, the same as
Ctrl+P over files, so only the letters in order are needed
- Every column is also addressable as
Column N, so files without a header row work the same way
- A row number larger than the file is refused with the file's real row count, never clamped onto the last row
- Works while a large file is still being read, and the number becomes reachable as soon as the scan gets there
- Clicking the row count or the cursor position in the status bar opens it too
Editing and Saving
F2, typing, or the toolbar's Edit opens an editor over the active cell; Enter, Tab, and Shift+Tab commit and move, Escape cancels
Ctrl/Cmd+S writes the file, with VS Code's own unsaved dot, close prompt, undo, redo, and revert
- Saving rewrites only the rows you edited and copies every other byte verbatim, preserving byte-order marks, mixed line endings, a missing trailing newline, and the original author's quoting
- Editing one cell changes exactly one line of the file, and within it exactly one field; both are property-tested over a corpus of awkward files
- The file is replaced atomically by a flushed temporary file written beside it, so a failed save leaves the original exactly as it was and never leaves a temporary file behind
- Saving through a symbolic link replaces the link's target, and the file's permissions are preserved
- A value the file's quoting mode cannot represent is refused as you commit it, naming the row and column, with your text still in the editor
- Unsaved edits survive a VS Code restart, and are discarded with a notice if the file changed on disk in the meantime
Selection and Copying
- Click a cell to place the active-cell cursor; navigate with arrows,
Tab, Home, End, and page keys
- Extend rectangular cell selections with
Shift, or select and toggle full rows from the row-number gutter
- Select all currently indexed data rows with
Ctrl/Cmd+A
- Copy with
Ctrl/Cmd+C or the toolbar; Ctrl/Cmd+Shift+C also includes matching headers
- Copy preserves the active delimiter and detected line-ending style, with bounded host-side serialization
Usage
Opening Files
Install Tabulify in Visual Studio Code, then drag and drop or open a file and use any of the following methods to view the data.
- Right-click method: Right-click any delimited file in the Explorer and select "Open as Table"
- Command Palette: Use
Ctrl+Shift+P and search for "Tabulify: Open as Table"
- Editor title: Click the "Open as Table" button in the editor title bar when viewing a delimited file
Quick Demo
Opening and scrolling a large file

Opening a large file and scrolling it. The Marketplace strips embedded players, so the poster above links to the video on GitHub.
Dark theme

Light theme

Inspector panel

Cell editor

A JSON Lines file open as a table, with the Inspector showing a nested value in full:

Keyboard Shortcuts
- Arrow keys,
Tab, Home, End, Page Up, Page Down - Move the active-cell cursor
Shift plus arrows, Home, End, Page Up, or Page Down - Extend a rectangular selection
Ctrl/Cmd+A - Select all currently indexed data rows
Ctrl/Cmd+C - Copy the selection
Ctrl/Cmd+Shift+C - Copy the selection with matching headers when Has Header is enabled (tabulify.copyWithHeader)
Ctrl/Cmd+F - Open the search bar; Enter and Shift+Enter step through matches while it has focus
Ctrl/Cmd+I - Open or close the Inspector panel
Ctrl/Cmd+G - Jump to a row by number, or to a column by name
F2 or any printable character - Edit the active cell
Enter, Tab, Shift+Tab - Commit an edit and move; Escape cancels it
Ctrl/Cmd+S - Save the file; Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z undo and redo one cell at a time
Enter - Expand the focused row over the grid, or collapse it
Escape - Close the expansion overlay, or collapse the selection to the active cell
Requirements
- Visual Studio Code version 1.103.0 or higher
Tabulify runs on the desktop only. Large-file support requires Node positional file reads, which VS Code for the Web cannot provide.
Commands
tabulify.openAsTable: Open the current file as a table view
tabulify.toggleInspector: Open or close the Inspector panel, bound to Ctrl/Cmd+I. Applies only while a Tabulify editor is focused.
tabulify.revealRawLine: Open the Inspector on its Raw tab for the row under the cursor. Applies only while a Tabulify editor is focused.
tabulify.goTo: Jump to a row by number or to a column by name, bound to Ctrl/Cmd+G. Applies only while a Tabulify editor is focused, so Ctrl+G remains VS Code's own Go to Line elsewhere.
tabulify.copyWithHeader: Copy the selection with the matching header cells above it, bound to Ctrl/Cmd+Shift+C. Applies only while a Tabulify editor is focused. With Has Header off there is no header row to include and it copies the selection alone.
fileViewer.openAsTable: Deprecated alias for the above, kept so existing keybindings keep working. It still functions and forwards to the new command; it no longer appears in any menu. See docs/usage.md if you want to move a custom keybinding over.
Extension Settings
tabulify.largeFileThresholdMB (number, default 32): files at or below this size are held in memory; larger files are read from disk on demand so they never have to fit in memory. Changes apply to files opened afterward.
tabulify.defaultDelimiter (string, default ,): the field separator. Accepts multi-character values such as :: and the escape sequences \t, \n, \r, \b, \f, \v, \0, and \\, up to 32 bytes once decoded. Changing it re-indexes any open table.
tabulify.quoting (auto | rfc4180 | none, default auto): how quoted fields are recognized. Under auto, RFC 4180 quoting is used unless a scan of the start of the file finds unbalanced quotes, in which case quoting is disabled for that file. Changing it re-indexes any open table.
tabulify.rowHeight (compact | normal | comfortable, default normal): selects the fixed grid row height for newly opened files.
tabulify.maxColumnWidth (number, default 250): caps automatic column sizing from 50 to 2000 pixels for newly opened files.
Documentation
- Quickstart — installing, building, and running the tests
- Usage — commands, settings, and diagnostics
- Architecture — layer boundaries, loading strategies, the document lifecycle, selection, and copying
- Document Core and File Access — what Tabulify detects about a file and which files it refuses
- Row Index and Chunked Scanner — how rows are located, how delimiters and quoting are resolved, and what the row and column limits are
- Parser and RFC 4180 Writer — how fields are read and written, what makes a row malformed, and the byte-preservation guarantees
- Host and Webview Protocol — the typed message contract between the extension and the panel, the two-bundle build, and the security rules that govern the webview
- Virtualized Grid — progressive rendering, row recycling, scroll-space compression, and column sizing
- Cursor, Selection, and Copy — keyboard and pointer controls, clipboard formatting, limits, and privacy
- Cell Inspector and Row Expansion — the Inspector panel, the raw-line view, focused-row expansion, and the scroll invariant that makes it safe
- Find — full-file search, smart case, regular expressions, column scope, match navigation, highlighting, and the limits that keep it responsive
- Go-To — the row jump, the fuzzy column jump, the coordinate rule, and what happens when a number does not fit
- Filter — per-column and global terms, what a term means, progressive counts, and how every other feature follows the filtered view
- Editing and Byte-Preserving Save — the cell editor, undo and revert, what a save preserves, how the atomic write works, and what unsaved edits survive
- JSON and JSON Lines — which shapes become tables, how records are found in each format, how values are displayed, the conformance notes, and the read-only boundary
Development
npm install
npm run compile
npm run test:unit
npm test
Press F5 in VS Code after compiling to launch the Extension Development Host. See the Quickstart for setup details and the known Windows integration-test path limitation.
Contributing
For new issues or feature requests, please open a GitHub issue. And don't forget to star the repository!
Current Limitations
- JSON and JSON Lines files are read-only. The editing controls are absent for them, and the file is never written.
- Nested values inside a JSON record are one cell each; they are not flattened into their own columns.
- Tabulify does not claim JSON as a default editor, so opening one as a table is always a deliberate choice from the context menu, the Command Palette, or Reopen Editor With.
- A JSON file holding a single value, and a list mixing objects with lists or plain values, have no table in them and are refused with an explanation.
- If an ordinary JSON file's structure breaks partway through, the rows read before that point are shown and the file is reported as stopping early; a JSON Lines file isolates a bad line instead.
- Editing is one cell at a time: there is no fill-down, no paste of a block into a range, and no multi-cell delete.
- Rows cannot be inserted or deleted.
- Copy uses the values in the file rather than your pending edits, so copying an edited cell before saving puts the on-disk value on the clipboard.
- An edit never re-runs the active filter, so a row whose new value no longer matches stays visible until the filter is re-applied.
- A successful save re-indexes the whole file, because every byte offset after the first edited row has moved. On a very large file that is the full scan again for a one-cell edit.
- Save As writes a copy and leaves the editor on the original file, with the edits still pending.
- A search covers the whole file but stops at 100,000 matches, reported as
100,000+ rather than as a final count.
- Filter terms are literal substrings; there is no regular-expression or expression filter mode.
- Under an active filter the row-number gutter shows the position in the filtered view, not the row's number in the file.
- Filtering a 500MB file completes in around fifteen seconds; matching rows appear within milliseconds and the count climbs throughout.
- The Inspector panel takes a fixed share of the editor height and cannot be resized by dragging.
License
Tabulify is available under the MIT License.
Release Notes
0.2.0 adds JSON and JSON Lines as read-only table formats and leaves every delimited
behavior unchanged. 0.1.0 was the first release since 0.0.5 and a substantial rewrite. See
CHANGELOG.md for everything that changed in each, including the two v0.0.5
behaviors that were deliberately replaced.
Enjoy exploring your file data in a clear, readable format.
| |