feat: add Pane Search Phase 1 checkpoint (v1.3.1) - #44
Merged
Conversation
Bind Ctrl+F to the Find action so search opens in the active pane, keeping Ctrl+Shift+F as a compatibility alias. The existing Microsoft Terminal search pipeline (SearchBoxControl, ControlCore::Search, TextBuffer::SearchText, renderer highlights) carries the feature unchanged: live search over the pane's scrollback, all-match highlighting, Enter/Shift+Enter navigation with wrap-around, Esc cleanup, and per-control state isolation across split panes. Add ActionMap defaults coverage for both Find chords and user unbinding, plus ControlCore tests for all-match spans, case behavior, no-match and empty-needle cleanup, navigation wrap-around, and per-core search-state isolation. Advance the engineering version to 1.3.1 following the v1.2.x checkpoint convention (stable channel, no prerelease suffix, package 1.3.1.0), add v1.3.1 to the release workflow checkpoint-tag list, update every pinned version literal in the verification scripts, and document Ctrl+F in CHANGELOG.md and docs/user/keyboard-shortcuts.md.
The layered ActionMap scenario now mirrors the defaults.json shape (one action definition plus separate keybinding entries) instead of the legacy command+keys form, whose in-box entries do not resolve through GetActionByKeyChord. The all-match span assertion now expects the exclusive end column reported by TextBuffer::SearchText (5, one past the last character of "error"), matching the observed TAEF run.
Owner
Author
|
Local compiled test results (x64 Release, TAEF):
Two corrections landed in 9f4a918 after the first local run:
Full-suite regression runs over both test DLLs are in progress; results will follow in this thread. |
The test proved a fragment keys field is ignored by asserting Ctrl+F resolved to nothing, which relied on that chord being unbound in the inbox defaults. Ctrl+F is now the default Find binding, so the test asserts the stronger form of the same contract: the chord still resolves to the in-box Find action rather than the fragment one.
Owner
Author
|
Full-suite regression results (x64 Release, TAEF, local):
Combined with the earlier targeted runs, every compiled suite touching this change now passes locally: KeyBindingsTests 20/20, ControlCoreTests 20/20, full Control DLL 77/77, full SettingsModel DLL 244/244. |
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1 of the Pane Search roadmap toward v1.4:
Ctrl+Fnow opens Find inthe active pane. This is engineering checkpoint
1.3.1— not an alpha,not a beta, and not a downloadable milestone. GitHub Latest and WinGet keep
pointing at v1.2.0, and v1.3.0-beta3 remains the newest published
prerelease.
Root cause of "Ctrl+F does nothing":
defaults.jsonnever boundctrl+fto anything, so the chord fell through the ActionMap and
TermControlforwarded a raw
^F(0x06) to the shell connection. The mature upstreamsearch pipeline behind
Ctrl+Shift+Fwas fully functional the whole time.Do not add the
buildlabel — Phase 1 is not a downloadable milestone,and quick validation is the intended CI depth for this PR.
Related issues
None — roadmap work item (Pane Search Phase 1).
Detailed changes
Implementation
The feature change is a single default keybinding in
src/cascadia/TerminalSettingsModel/defaults.json:{ "keys": "ctrl+f", "id": "Terminal.FindText" }, { "keys": "ctrl+shift+f", "id": "Terminal.FindText" },Ctrl+Shift+Fstays bound as a compatibility alias — the ActionMapnatively supports several chords per action (CopyToClipboard already has
three). Both chords remain ordinary remappable keybindings; a user who
needs a literal
^Ffor a terminal application can unbindctrl+f(
{ "command": "unbound", "keys": "ctrl+f" }) and keep Find on the aliasor the Command Palette. No XAML key handling was added; dispatch uses the
normal action pipeline. The winTerm Ctrl+C reservation fixup is scoped to
origin == User && id == Terminal.CopyToClipboard && keys == "ctrl+c"anddoes not touch this binding, and
ctrl+fcollides with no winTerm customshortcut (
ctrl+t,ctrl+tab,ctrl+,,ctrl+0, …).Search architecture reused
No second search engine, no index, no buffer duplication, no
src/winterm/Searchsubsystem. The existing pipeline carries everything:SearchBoxControl— the pane-local overlay (x:Load="False"lazy XAMLelement inside
TermControl), opened byTermControl::CreateSearchBoxControl(), which focuses the TextBox andpreserves the single-line selection-populates-search behavior.
TextBoxTextChanged→SearchChanged→TermControl::_SearchChanged→ControlCore::Search(ExecuteSearch=false)→
Search::Reset→TextBuffer::SearchTextover the full searchablebuffer including scrollback.
Search::Results()(astd::vector<til::point_span>of every match) flows intoTerminal::SetSearchHighlights;SetSearchHighlightFocusedmarks thecurrent match and
Renderer::TriggerSearchHighlightinvalidates theaffected regions. The existing scrollbar pips read the same rows.
Search→ControlCore::Search(ExecuteSearch=true)→Search::FindNext, whichwraps modulo the match count.
_CloseSearchBoxControl→ControlCore::ClearSearch()clears highlights, resets the searcher, andrefocuses the terminal.
TermControl::_KeyHandlerreturns early while_searchBox->ContainsFocus(), so search-box keystrokes never reach theconnection; the TSF/IME focus guard does the same for composition input.
TextBuffer::SearchTextreturns no results for empty orall-whitespace needles without doing any expensive work, so an emptied
search box drops all highlights.
Search stays demand-driven: with the box closed there is no polling, no
timer, and no background scan (
Search::IsStalere-evaluates only on thenext explicit search request).
Active-pane behavior
ShortcutAction::Find→TerminalPage::_HandleFind→_senderOrFocusedTab()→TerminalPage::_Find(tab)→Tab::GetActiveTerminalControl()→_activePane->GetLastFocusedTerminalControl()→CreateSearchBoxControl(). The existing pane model stays the single sourceof truth — no new "active search pane" state was introduced. Each
TermControlowns its ownSearchBoxControland itsControlCoreownsits own
Searchinstance, so search state in one split pane cannot bleedinto a sibling. Pressing
Ctrl+Fafter moving pane focus targets the newlyactive pane.
Version change
1.3.0-beta3→1.3.1, an engineering checkpoint following the exactv1.2.1–v1.2.4 Command Timeline precedent: plain version,
channel: stable, empty module prerelease, package version1.3.1.0, andtag
v1.3.1added to the release workflow's checkpoint-tag list, so apushed checkpoint tag runs quick validation only and can never produce
release artifacts or move Latest. All version surfaces advanced together:
both
version.jsonfiles,ReleaseMetadata.h, the MSIX manifest, three.rcfiles, the PowerShell module manifest and runtime, the workspacedescriptor/serializer fallbacks, the pinned literals in the verification
scripts (
verify-version,verify-branding,test,test-visual-progress,package-shell-assets,test-release-workflow), both READMEs'source-version lines,
docs/current-progress.md, andCHANGELOG.md.Documentation
CHANGELOG.md:1.3.1checkpoint section (Ctrl+F active-pane search,live search, all-match highlighting, pane isolation, pipeline reuse); the
previous
Unreleaseddocumentation notes fold into this section.docs/user/keyboard-shortcuts.md: new Search section documentingCtrl+F, the alias, navigation, close behavior, input isolation, and howto reclaim raw
Ctrl+F.docs/current-progress.md: repository state for the checkpoint and thePane Search roadmap status.
Development-Changes.mdledger entry follows this PR (needs thefinal SHA and PR number).
Known limitations / deferred Phase 2+ work
Explicitly deferred, by design: scrollbar overview markers, the
WinTerm-specific search UI redesign, performance hardening (debounce,
worker threads, caching), and large-scrollback optimization. The stock
upstream search box visuals and status display remain as-is for this
phase. Out of scope entirely: cross-tab/global/all-pane search, Command
Timeline search, persistent history, fuzzy/semantic/AI search, telemetry.
Validation performed
Environment: Windows 11 x64, MSVC 14.44 + Windows SDK (local toolchain).
pwsh scripts\winterm\verify-version.ps1— passed (all assertions,including the new 1.3.1 literals and checkpoint-tag list).
pwsh scripts\winterm\verify-branding.ps1— passed.pwsh scripts\winterm\test.ps1 -Suite Smoke -Configuration Release -Platform x64— passed (same gate CI quick validation runs; includes static,
identity, asset, license, shell, workspace, pane, release-workflow, and
privacy checks).
UnitTests_SettingsModel(
KeyBindingsTests::FindDefaultShortcutsAndUserOverride) andUnitTests_Control(ControlCoreTests::TestSearchHighlightsAllMatches,TestSearchNavigationAndClear,TestSearchStateIsolatedPerCore). Thex64 Release builds of both test DLLs are running locally now; te.exe
results will be posted in this PR's conversation before merge. CI's
no-label quick validation intentionally does not compile native tests.
New test coverage:
ctrl+fandctrl+shift+ftoShortcutAction::Find; a user layer can unbindctrl+fwhile the aliasstays bound.
foo/error/bar/errorbuffer yields 2 matches on the live-typing pathwith both spans (rows 1 and 3) present in
SearchResultRows();case-insensitive by default; case-sensitive mismatch, no-match needle,
and empty needle each yield 0 matches and an empty highlight collection.
0→1→0; Shift+Enter wraps backward;
ClearSearch()empties the spans.ControlCoreinstances keep fully independent search state (thesplit-pane isolation acceptance case at the state level).
Manual smoke checklist (PowerShell) for user validation:
1..20 | ForEach-Object { Write-Host "line $_ ERROR test" }, pressCtrl+F, typeERROR: the search box opens focused inside the pane,every occurrence highlights, Enter/Shift+Enter step forward/backward
with wrap-around, Esc closes and clears the highlights.
Ctrl+Finpane A searches and highlights only pane A; focusing pane B and pressing
Ctrl+Fgives pane B its own independent search box and state.scrolled out of view: it is found and scrolled into view.
Enter in the shell: the typed query never appears on the command line.
Ctrl+Shift+Fstill opens the same search box.Checklist
CHANGELOG.md.Development-Changes.mdledger has been pushed with the final source SHA, link, summary, and checkpoint/release.winterm-siteasset, or no screenshot was added.winterm.exe, and Microsoft Terminal coexistence remain isolated.