Skip to content

feat(editor): add Find and Replace and Use Selection for Find to the Edit menu - #2893

Merged
datlechin merged 9 commits into
mainfrom
feat/editor-find-and-replace
Sep 15, 2026
Merged

datlechin merged 9 commits into
mainfrom
feat/editor-find-and-replace

Conversation

@datlechin

Copy link
Copy Markdown
Member

Stacked on #2886.

The editor has had a working Replace mode since it was vendored, reachable only from a popup
inside the find panel itself. There was no menu item, no key equivalent, and showFindPanel()
reset the panel to .find on every open, so the replacement field went away and the replacement
text with it. This puts both of macOS's remaining Find commands where macOS puts them.

Edit > Find

The submenu now reads in Apple's order, on Apple's keys:

Item Key
Find… Cmd+F
Find and Replace… Cmd+Option+F
Find Next Cmd+G
Find Previous Cmd+Shift+G
Use Selection for Find Cmd+E

Both new items are nil-targeted like the other three, so a focused editor answers for itself
through the responder chain and a window with no focused editor falls through to
MainSplitViewController, which dims them. Neither falls back to the result grid's find bar: the
grid has no replacement field, and a selected cell is a value rather than a search term.

Cmd+Option+F changes hands

That combo is Apple's Find and Replace key and TablePro had it on Toggle Filters, which moves
to Cmd+Shift+F (free, and next to Find). This is the one user-visible decision in the PR and it
is easy to reverse; both are rebindable in Settings > Keyboard, and anyone who has already rebound
either keeps their mapping. The alternative was giving a documented text-editing shortcut a
non-standard key, which is the thing "native, no custom ways" rules out.

The conflict is real rather than theoretical: the find commands are .global context because they
dispatch by focus, and .global overlaps every other context, so AppKit would have blanked one of
the two items with no warning.

Behaviour

  • Find and Replace opens the same panel in replace mode, or switches an already-open panel to it.
  • The panel now keeps whichever mode it was left in. showFindPanel(mode:) takes the mode as an
    argument instead of resetting; Cmd+F passes none.
  • Use Selection for Find makes the selection the search term and stops there: no panel, no
    caret movement, which is what macOS does. Cmd+G is what walks to the next match afterwards.
    It is disabled with an empty caret, which needs NSMenuItemValidation on TextViewController,
    because AppKit stops validating at the target it resolved and never asks
    MainSplitViewController for a focused editor. The conformance is @retroactive, since the app
    owns neither TextViewController nor NSMenuItemValidation.

It does not write the system find pasteboard. TablePro's panel is its own state rather than
NSTextFinder's, so writing a pasteboard nothing here reads back would be half a feature.

Tests

  • FindAndReplaceEntryTests (package): opening with no mode keeps the last mode, opening in
    replace mode gives the 54pt panel, naming a mode switches an already-open panel, and the
    replacement text survives a close and reopen.
  • UseSelectionForFindTests (package): an empty caret offers nothing, the selection becomes the
    term and finds both matches, the panel stays shut and the caret stays put, and an empty
    selection leaves an existing term alone.
  • FindMenuItemsTests (app): the five items in order, both new ones nil-targeted on the right
    keys, and both commands .global in the .editor category.
  • MainMenuBuilderTests.findAndFilterDefaultsHold now pins all five find keys plus the filter
    bar's new one.

swift test --package-path Packages/TableProEditor --force-resolved-versions: 333 + 110 cases,
0 failures. swiftlint lint --strict: 0 violations. verify.sh docs: PASS.

FindPanelMode.displayName is localized, and Find and Replace…, Find and Replace and
Use Selection for Find are in the strings catalog with all five languages, matching how the
neighbouring find strings are translated.

MainSplitViewController+MenuValidation.isEnabled was two lines under the 160-line limit, so the
find cases moved to isFindCommandEnabled, which is where they read better anyway.

…package

# Conflicts:
#	CLAUDE.md
#	LocalPackages/CodeEditTextView/Sources/CodeEditTextView/CodeEditTextView.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Controller/TextViewController+TextViewDelegate.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Controller/TextViewController+ToggleComment.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Extensions/NSRange+/NSRange+InputEdit.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Extensions/TextView+/TextView+Point.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Extensions/TextView+/TextView+TextFormation.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Extensions/TextView+/TextView+createReadBlock.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Filters/DeleteWhitespaceFilter.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Find/PanelView/FindPanelView.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Find/ViewModel/FindPanelViewModel+Replace.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/Find/ViewModel/FindPanelViewModel.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/SourceEditor/SourceEditor+Coordinator.swift
#	Packages/TableProEditor/Sources/TableProEditorKit/TextViewCoordinator/CombineCoordinator.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/CaptureModifierSetTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/CodeEditSourceEditorTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/Controller/TextViewController+IndentTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/Controller/TextViewController+MoveLinesTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/Controller/TextViewControllerTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/FindPanelTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/GutterNumberOffsetTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/Highlighting/StyledRangeContainerTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/Highlighting/VisibleRangeProviderTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldAccessibilityTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldChunkBoundaryTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldCollapsePathTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldDocumentSwapTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldHoverTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldPlaceholderClickTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldRibbonLookupTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldStorageTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/LineFoldingTests/LineFoldingModelTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/RangeStoreTests.swift
#	Packages/TableProEditor/Tests/TableProEditorKitTests/TreeSitterClientTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/AccessibilityTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/CmdUpAtEndOfDocumentTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/IMEInputTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/KillRingTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/LayoutManager/OverridingLayoutManagerRenderingTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/LayoutManager/TextLayoutManagerAttachmentsTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/LayoutManager/TextLayoutManagerTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/LineEndingTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/MarkedTextTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/NSBezierPathSmoothPathTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/NSTextInputRangeGuardTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/TextLayoutLineStorageTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/TextLineDisplayRangeTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/TextSelectionManagerTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/TextViewTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/TypesetterTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/VisualLineEndOfDocumentTests.swift
#	Packages/TableProEditor/Tests/TableProTextEngineTests/WordSelectionTests.swift
…replace

# Conflicts:
#	Packages/TableProEditor/Sources/TableProEditorKit/Controller/TextViewController+FindPanelTarget.swift
@mintlify

mintlify Bot commented Sep 15, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
TablePro 🟢 Ready View Preview Sep 15, 2026, 8:52 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@datlechin
datlechin merged commit 1593910 into main Sep 15, 2026
9 checks passed
@datlechin
datlechin deleted the feat/editor-find-and-replace branch September 15, 2026 08:57
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.

1 participant