Column Operations¶
Column splitting (_apply_column_delimiter)¶
When the user presses d on a column, nless splits that column's values
by a sub-delimiter to create new computed columns. This is how you go from
a single request column containing GET /api/users HTTP/1.1 to three
columns: method, path, protocol.
Flow¶
- User selects a column and enters a delimiter (string or regex)
- Sample the column's values to determine how many fields the split produces
- Generate column names:
{original}_{1},{original}_{2}, etc. - Create
Columnobjects withcomputed=True,col_refpointing to the source column, andcol_ref_indexindicating which split field - Insert before the
_arrivalmetadata column (so it stays last) - Trigger a full rebuild —
split_line()will usecol.delimiterandcol.col_ref_indexto extract the right field during parsing
JSON extraction¶
If the column contains JSON strings, the user can press J instead. This
works differently:
- Parse the JSON from the cell under cursor
- Present top-level keys as a select menu
- Create a computed column with
json_refset to the selected key - During parsing,
split_line()doesjson.loads(cell)[json_ref]
Nested JSON extraction repeats this process — the user can J on an
already-extracted JSON column to go deeper.
Computed column persistence¶
Computed columns survive session save/load. SessionComputedColumn stores:
- col_ref — source column name
- col_ref_index — which split field (-1 for JSON)
- json_ref — JSON key path
- delimiter / delimiter_regex / delimiter_regex_flags — how to split
Dedup / pivot (handle_mark_unique)¶
Pressing U on a column marks it as a unique key. Multiple U presses
build a composite key. The result is a pivot-like view showing distinct
values with counts.
How it works¶
copy()the buffer (new tab, preserves history)- Add the column name to
query.unique_column_names - Insert a
countmetadata column at position 0, shifting all other columns right by 1 (bothdata_positionandrender_position) - Add
"U"label to the selected column - If sort was on, and the sort column is now a unique key, clear it (dedup changes the row structure, sort may no longer make sense)
- Default sort: count descending (most common values first)
During rebuild¶
_dedup_rows() in buffer.py collapses rows by composite key:
- Build key from all unique column values (stripped of markup)
- Keep last-seen row for each key
- Prepend count to each row
- Track counts in query.count_by_column_key
Incremental streaming updates¶
When new rows arrive with dedup active, _add_log_line() checks if the
composite key already exists:
- Existing key: increment count, highlight the row green, remove old
row and re-insert at the correct sorted position
- New key: insert with count=1
This gives live-updating aggregation during streaming.
Column pinning¶
Pinning (m key) locks a column to the left side of the DataTable. Pinned
columns don't scroll horizontally. The DataTable renders them with
fixed_columns count.
Pin/unpin reorders render_position values — pinned columns get the lowest
positions, unpinned columns shift right. The "P" label is added/removed
from the column.
Key files¶
app_columns.py—_apply_column_delimiter(),_apply_json_header(),action_toggle_arrival(),_apply_column_filter()operations.py—handle_mark_unique(),compute_column_aggregations()buffer_actions.py—action_pin_column(),action_move_column(),action_sort()buffer_columns.py—ColumnMixin._make_columns(),_rebuild_column_caches()