Skip to content

feat(datagrid): copy column values plus Copy as CSV/Markdown/IN Clause (#1325)#1330

Merged
datlechin merged 2 commits into
mainfrom
feat/1325-copy-column-values-and-formats
May 19, 2026
Merged

feat(datagrid): copy column values plus Copy as CSV/Markdown/IN Clause (#1325)#1330
datlechin merged 2 commits into
mainfrom
feat/1325-copy-column-values-and-formats

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

Closes #1325 (能否添加复制多条id功能? — "Can you add a feature to copy multiple IDs, or copy an entire column?").

Adds two complementary entry points to the data grid:

  • Right-click column header → Copy Column Values — copies every value from the loaded rows of the clicked column, one per line. Operates on the currently visible page (matches every other copy action in the app).
  • Row context menu → Copy as submenu: new entries for CSV, CSV with Headers, Markdown table, and IN Clause. IN Clause produces (1, 2, 3) or ('a', 'b') from the right-clicked column across the selected rows, ready to paste into a WHERE col IN (...) query.

Native macOS pattern throughout — NSMenu / NSMenuItem with representedObject carrying column indices, dispatching to coordinator methods that mirror the existing copyRowsAsJson(at:) / copyRowsAsInsert(at:) shape. No new abstractions; new converters (CsvRowConverter, MarkdownTableConverter, InClauseConverter) live in Core/Utilities/SQL/ alongside the existing JsonRowConverter / SQLRowToStatementConverter and follow the same struct shape.

ClipboardService gains a writeCsv(_:) method that writes .string + utf8PlainText + public.comma-separated-values-text so pasting CSV into Numbers/Excel works without content-sniffing. writeText and writeRows are unchanged.

Design decisions

  • Column Values lives on the column header context menu, not the row Copy-as submenu. HIG-wise headers are for structural actions (sort, hide, resize) and Numbers/Mail follow that pattern. But here the action is self-explanatory and discoverable, similar to TablePlus's "Copy All Column Value". One menu item, no submenu.
  • IN Clause uses the right-clicked column, same idiom as the existing "Cell Value" entry. Reflects the user's natural mental model when copy-pasting from a specific cell into a WHERE clause.
  • Scope is loaded rows only, not "all matching rows in the database". Matches every other copy operation in the app. No background queries, no surprise costs.
  • NULL handling differs by format on purpose — CSV: empty field (RFC 4180 / Postgres COPY default). Markdown: NULL literal (matches grid display). IN Clause: NULLs are filtered out — WHERE x IN (.., NULL, ..) never matches NULL rows in three-valued SQL logic anyway, so emitting them would silently mislead the user.
  • No new keyboard shortcuts. Cmd+C (TSV) and Cmd+Shift+C (TSV with Headers) stay default. Cmd+Opt+C is reserved by macOS for "Copy Style" in text contexts and would conflict in the SQL editor.

Code centralization

Following the existing copyRowsAsJson(at:) pattern exactly:

// TableViewCoordinator extension (DataGridView+RowActions.swift)
func copyRowsAsCsv(at:includeHeaders:)
func copyRowsAsMarkdown(at:)
func copyRowsAsInClause(at:columnIndex:)
func copyColumnValues(columnIndex:)

All three converters use the driver-provided escapeStringLiteral closure when the column needs SQL quoting — same pattern as SQLRowToStatementConverter.

Files changed

  • New converters: CsvRowConverter.swift, MarkdownTableConverter.swift, InClauseConverter.swift
  • New tests: CsvRowConverterTests, MarkdownTableConverterTests, InClauseConverterTests (24 tests, all passing)
  • ClipboardService.swift — new writeCsv(_:) plus protocol method; existing mocks updated
  • DataGridRowView.swift — 4 new menu items + actions, dedup helper selectedOrCurrentIndices(in:) covering 8 existing call sites that had the same guard/ternary pattern duplicated
  • DataGridView+Sort.swift — 1 new header-menu item + action
  • Localizable.xcstrings — Xcode auto-extracted new String(localized:) keys
  • docs/features/data-grid.mdx — full Copy as / Copy Column Values table, plus a correction (Cmd+Shift+C was documented as "CSV"; it's actually TSV with Headers)

Verification

  • 24 new converter tests pass; all existing copy/paste tests in RowOperationsManagerCopyTests + CellPasteRoutingTests still green
  • Build clean against the main TablePro scheme
  • No banned filler words in CHANGELOG / docs / PR body

Test plan

  • Right-click a column header on a SELECT result, choose Copy Column Values, paste into a text editor — one value per line, length matches loaded rows.
  • Select 3 rows on an id INT column, right-click on the id cell, Copy as → IN Clause — clipboard contains (1, 2, 3).
  • Same flow with a name VARCHAR column — clipboard contains ('a', 'b', 'c') with proper quoting.
  • Copy as → CSV with a row containing O'Brien and doe, john — RFC 4180 compliant quoting.
  • Copy as → Markdown — pipe-escaped cells render correctly when pasted into GitHub/Discord.
  • Existing Cmd+C (TSV) and Cmd+Shift+C (TSV with Headers) behavior unchanged.

@datlechin datlechin merged commit 529c06f into main May 19, 2026
1 check passed
@datlechin datlechin deleted the feat/1325-copy-column-values-and-formats branch May 19, 2026 05:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

能否添加复制多条id功能?

1 participant