Skip to content

Release v2.2.0-rc.1#5324

Merged
tig merged 99 commits into
mainfrom
release/v2.2.0-rc.1
May 17, 2026
Merged

Release v2.2.0-rc.1#5324
tig merged 99 commits into
mainfrom
release/v2.2.0-rc.1

Conversation

@tig
Copy link
Copy Markdown
Member

@tig tig commented May 17, 2026

Release v2.2.0-rc.1

This is a rc pre-release.

Version: 2.2.0-rc.1
NuGet Package: Terminal.Gui 2.2.0-rc.1

What happens when this PR is merged

  1. ✅ The Finalize Release workflow will automatically create tag v2.2.0-rc.1
  2. ✅ The Publish workflow will build and push to NuGet.org
  3. ✅ A GitHub Release will be created with auto-generated notes
  4. ✅ A back-merge PR from maindevelop will be opened

Checklist

  • CI passes on this PR
  • Version looks correct: 2.2.0-rc.1
  • Release notes reviewed (will be auto-generated on merge)

Copilot AI and others added 30 commits May 8, 2026 03:44
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/259ae3cf-ff29-45bc-83a9-ba299adadaff

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/259ae3cf-ff29-45bc-83a9-ba299adadaff

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/259ae3cf-ff29-45bc-83a9-ba299adadaff

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Back-merge v2.1.0 from main into develop
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Map char offsets back to columns in SyncAutoUrlsForRowCore so that
  multi-codepoint graphemes (ZWJ emoji, base + combining mark) before a
  URL no longer shift the auto-link metadata onto the wrong cells.
- Null out _explicitUrlMap and _autoUrlMap in ClearContentsCore (and when
  SyncAutoUrlsForRowCore leaves _autoUrlMap empty) so GetCellUrl's null
  fast-path stays effective and RowContainsUrls doesn't acquire the
  contents lock per cell on URL-free buffers. Bump a UrlStateVersion
  counter so OutputBase can detect resets.
- Track buffer reference, dimensions, and UrlStateVersion in OutputBase;
  drop _rowsWithUrls when any of those change so resize/clear no longer
  leaves stale row indices that would emit a spurious OSC 8 close at the
  start of the next render.
- Refresh the Write XML summary that still mentioned WrapOsc8.
- Add regression tests for the grapheme alignment, post-resize state,
  and GetCellUrl fast-path restoration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ity (#5279)

* Fixes #5127. Anchor fixed-width button delimiters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fixes #5127. Keep button highlight continuous

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fixes #5127. Refine OnDrawingText: cache GetDrawRegion, FillRect for full highlight

- Cache _interiorTextFormatter.GetDrawRegion result; pass to GetDelimiterRow
  instead of recomputing it, eliminating a redundant layout pass.
- Replace Driver.AddStr(new string(' ', width)) with Driver.FillRect(drawRect)
  so focus highlight covers all rows of a multi-row button, not just the text row.
- Remove redundant Region.Combine union (interior region is always a subset of drawRect).
- Add comment linking GetDecoratedText / GetInteriorText dual-path responsibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: guard _interiorTextFormatter assignments, mirror missing TextFormatter settings, add multi-row highlight test

- Guard every _interiorTextFormatter property assignment with a value-equality check
  so NeedsFormat is not set spuriously on unchanged values
- Mirror MultiLine, WordWrap, TabWidth, and PreserveTabs from TextFormatter
  to _interiorTextFormatter (previously unmirrored, potential behavioral regression)
- Add Focused_FixedWidth_Button_MultiRow_Highlight_Is_Continuous test verifying
  all rows carry the Focus attribute when Height > 1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add ButtonDrawBenchmark to measure OnDrawingText guard performance

Adds Tests/Benchmarks/Views/ButtonDrawBenchmark.cs with two methods:
- DrawButton_Unchanged: steady-state draw (no property changes) — the
  guard-optimized path where NeedsFormat is never set spuriously
- DrawButton_TextChanging: text rotates each iteration — forces a real
  reformat, approximating the old unguarded behaviour

[MemoryDiagnoser] exposes the allocation difference between the two paths,
quantifying the benefit of the guards added in the preceding commit.

Run: dotnet run --project Tests/Benchmarks -c Release -- --filter "*ButtonDraw*"

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…5282)

* Fixes #5273. Code block partial selection should not include fence delimiters

When copying a partial selection that starts or ends inside a fenced code
block, the copied text should not include the fence delimiters unless the
selection actually crosses from non-code content into the code block.

Root cause: GetSelectedText() unconditionally injected the opening fence
whenever it first encountered a code-block line, and unconditionally
injected the closing fence at the end of the loop if still inside a code
block. This meant any selection touching a code block line would include
fences, even if the selection was entirely within the code block.

Fix: Replace the unconditional fence injection with two tracking flags:
- selectionHasNonCodeContent: set true when any non-code line is processed.
  Opening fence is only emitted when transitioning from non-code -> code.
- codeOpenFenceEmitted: tracks whether an opening fence was actually
  emitted for the current code block. Closing fence is only emitted when
  the matching opening fence was emitted.

This ensures:
- Selection entirely within a code block -> no fences (regardless of position)
- Selection starting before a code block -> opening fence included
- Selection ending after a code block -> closing fence included

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify the comment to reflect the adjacent-code-block case

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fixes #5270. Correct screen-row lookup in DrawSelectionOverlayOnSubViewRows

ContentToScreen expects content-relative coordinates (where 0 = top of
the content area), not viewport-relative coordinates.  The old code
passed drawRow (= lineIdx - Viewport.Y), so ContentToScreen subtracted
Viewport.Y a second time, reading graphemes from the wrong screen row.
With a non-zero scroll offset this caused the selection overlay to copy
characters from an incorrect row (e.g. the table header) over the
correct row (the table body), making body cell values disappear.

Fix: pass lineIdx (content-relative) instead of drawRow.

Added regression test SelectionOverlay_On_Table_Is_Synced_When_Scrolled
that scrolls a Markdown view past introductory text so only a table is
visible, activates a full selection, and asserts the body-row values
(1, 2) remain in the screen buffer.  The test height (5) is intentionally
less than the total content height (7) so Viewport.Y = 2 is not clamped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix partial selection highlight on code-block and table rows

DrawSelectionOverlayOnSubViewRows was applying the selection attribute to
every column of a subview row unconditionally.  On the start/end lines of
a selection this caused unselected columns to be highlighted, making it
look as though the entire line was selected even when only part of it was.

Fix: call IsInSelection(lineIdx, col + Viewport.X) per column, mirroring
the per-grapheme check already in DrawRenderedLine for plain text lines.
For non-selected columns the original Cell.Attribute from ScreenContents
is restored so the subview styling is preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix selection overlay erasing popover content in code blocks

DrawSelectionOverlayOnSubViewRows reads graphemes from ScreenContents
(previous frame) and re-draws them with the selection attribute.
Popovers draw before MarkdownView in the application draw loop, so
their menu items are already written to the screen buffer when the
overlay runs. Re-drawing those cells with stale ScreenContents graphemes
silently erases the popover's content.

Fix: compute the active popover's content view screen rect before the
cell loop and skip any cell that falls inside it.  The popover's draws
are preserved; the selection highlight still covers all other cells.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…enu (#5283)

Remove the base class Ctrl+LeftButtonReleased -> Command.Context mouse binding
from Markdown.SetupBindingsAndCommands(). The base View registers this binding
so Ctrl+Click can open a context menu, but the Markdown view already handles
right-click directly in OnMouseEvent and uses Ctrl+Click for link following.
Without this removal, Ctrl+Click would fire Command.Context -> ShowContextMenu()
via the LeftButtonReleased event, showing the Select All/Copy popover at the
upper-left corner (because no selection is active, GetContextMenuScreenPosition
falls back to Point(0,0)).

Add two regression tests:
- MouseBindings_CtrlLeftButtonReleased_IsNotBoundTo_Context: verifies the
  binding is absent on a fresh Markdown instance
- CtrlClick_On_Link_Opens_Link_And_Does_Not_Show_Context_Menu: end-to-end
  verification that Ctrl+Click fires LinkClicked without making the context
  menu visible

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…context menu (#5285)

When the user right-clicks on a hyperlink in the Markdown view, the
context menu now includes a 'Copy Link' item at the top (followed by a
separator) that copies only the raw URL to the clipboard — not the
display text.

- Add _contextMenuLinkUrl field and FindLinkUrlAt helper (Mouse.cs)
- ShowContextMenu detects link under right-click position and rebuilds
  context menu accordingly (Selection.cs)
- CreateContextMenu prepends 'Copy Link' + separator when a link URL
  is detected (Selection.cs)

Keyboard context menu (Shift+F10) is unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#5289)

Move value-update logic from OnMouseEvent into a Command.Activate
handler bound via MouseBindings.Add. This makes mouse interaction
cancellable by external code (e.g. TerminalGuiDesigner) which can
clear the bindings.

Add GrabMouse/UngrabMouse in OnMouseEvent so drag events are routed
exclusively to the originating bar, preventing cross-bar drag
contamination.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch ConcurrentDictionary to Dictionary for _attributeCache
- Use integer math for scaling logic to avoid double conversions
- Implement reusable buffer for scaled image to reduce allocations during resize
The condition added in 56eef0c ("emit OSC 8 close at row start whenever
the row had URLs previously") was a defensive workaround for the apparent
floating-underline symptom on Warp/Windows Terminal. Investigation of WT
source shows the bug is in ControlCore::_updateHoveredCell()'s stale
_lastHoveredCell cache (no callback fires when buffer content changes
under a stationary cursor) — not in Terminal.Gui's emission. The cells
themselves get _hyperlinkId = 0 correctly via AdaptDispatch::EndHyperlink
when we emit `OSC 8 ; ; ST` followed by new cell content.

Restoring the original (rowHadUrlsPreviously && \!rowHasUrlsNow) condition
avoids a redundant escape on every row that still contains a URL after
redraw. Also drops the regression test that pinned the redundant behavior.

Separately, fix a real bookkeeping bug: _rowsWithUrls.Add/Remove was
placed after the empty-builder early-exit at the end of the per-row block.
Rows whose dirty cells were entirely flushed mid-loop via WriteToConsole
(leaving the builder empty and _lastUrl null) skipped the row-tracking
update, leaving stale entries that trigger spurious row-start OSC 8 closes
on subsequent frames. Move the Add/Remove before the early-exit, and add
a regression test that fails without the fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#5290)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactored all scrolling benchmark and performance test classes to follow project code style:
- Adopted uppercase constants for screen dimensions.
- Reordered and grouped setup/cleanup and Params for clarity.
- Used explicit types and target-typed new() per guidelines.
- Moved PageDown_OneStep benchmarks for logical grouping.
- Added new ListView performance tests for large datasets.
- Relocated helper methods and updated var usage for built-in types.
No functional changes; all updates are for style, structure, and maintainability.
Copilot AI and others added 16 commits May 16, 2026 23:25
DeriveAccent now copies every Code* attribute from baseScheme (resolved via GetAttributeForRole) into the accent scheme as explicitly set values. Because GetAttributeForRole walks the derivation chain, even unset code-token roles on the base scheme become explicit on the accent. This means: (a) accent schemes will report TryGetExplicitlySetAttributeForRole(VisualRole.CodeXxx) as true even though the original theme didn't set them, and (b) future changes to baseScheme's Code attribute will no longer propagate to the accent's code tokens because the accent now pins concrete copies. Consider only copying values that were explicitly set on the base scheme (using TryGetExplicitlySetAttributeForRole) so derivation continues to work.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/89b866c3-6b09-44f6-b2b0-e4f10515d475

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gui-cs/Terminal.Gui/sessions/89b866c3-6b09-44f6-b2b0-e4f10515d475

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e-vocabulary

Add code-token VisualRoles, Code view, theme palettes, and Markdown highlighting fixes
…onfig-manager

Fixes #5310. Add ConfigurationManager / Scheme / Theme benchmark baseline
The configurable keybinding surface (Application/View DefaultKeyBindings,
ViewKeyBindings, ApplyKeyBindings) is keyed by Command, so a View that
needs a command the enum doesn't define has no sanctioned way to register
it. Multi-caret editors (e.g. gui-cs/Editor's vertical multi-caret) need
"add caret above/below"; nothing in the enum fits, forcing magic-int
casts that don't round-trip through config by name.

Adds two named members mirroring VS Code's editor.action.insertCursor
Above / Below. Appended at the END of the enum so no existing member's
implicit value changes (serialization-stable; no renumbering).

No behavior change in TG itself — these are command ids for consumers to
AddCommand + bind. A general Command-extensibility API (string ids /
registry, bubbling/bridging) is tracked separately.

Refs: #5318. Related: #4888.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#5319 shipped the two enum members with no tests. Add coverage for the
parts of the contract that matter:

- CommandInsertCaretEnumTests: members are defined, distinct, carry their
  readable names, and are appended at the END of the enum (no existing
  member renumbered) — the property that keeps persisted configs stable.
- CommandInsertCaretKeyBindingTests: the #5318 acceptance criterion —
  Dictionary<Command, PlatformKeyBinding> round-trips through the config
  serializer (SourceGenerationContext) BY READABLE NAME, deserializes
  from the hand-written user-config form, and works nested under a
  per-view [ConfigurationProperty] dict ("Editor" → bindings) — exactly
  the gui-cs/Editor scenario the magic-int cast broke.

All 9 new tests green in UnitTests.Parallelizable. Mirrors the existing
KeyBindingSchemaTests style and uses the same canonical JSON options.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat: add Command.InsertCaretAbove / .InsertCaretBelow (#5318)
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8269e6c5ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Terminal.Gui/Views/ImageView.cs
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.

7 participants