From ff6c56eefa3c9950dced123cdb7e79fcdd8d080a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 13:43:10 +0000 Subject: [PATCH 1/6] Initial plan From 1ced6f4803f179c16453046b812e8e12a208223a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 13:50:20 +0000 Subject: [PATCH 2/6] feat: add UseThemeBackground property to Editor When true (the default), syntax-highlighted tokens use the base Normal background instead of the background supplied by the syntax highlighter, so the editor blends into the surrounding theme. Fixes the issue where the editor background was inconsistent with the theme's Normal background when syntax highlighting was active. Agent-Logs-Url: https://github.com/gui-cs/Text/sessions/30aff306-af60-4170-890c-2cde2267ebef Co-authored-by: tig <585482+tig@users.noreply.github.com> --- src/Terminal.Gui.Editor/Editor.cs | 25 ++++++++- .../Rendering/VisualLineBuildContext.cs | 9 +++- .../Rendering/VisualLineBuilder.cs | 11 +++- .../EditorRenderingTests.cs | 54 +++++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/src/Terminal.Gui.Editor/Editor.cs b/src/Terminal.Gui.Editor/Editor.cs index 1a446c2e..33114a23 100644 --- a/src/Terminal.Gui.Editor/Editor.cs +++ b/src/Terminal.Gui.Editor/Editor.cs @@ -231,6 +231,28 @@ public int IndentationSize /// Whether pressing Tab inserts spaces instead of a tab character. public bool ConvertTabsToSpaces { get; set; } + /// + /// When (the default), syntax-highlighted tokens use the base + /// background instead of the background supplied by the + /// syntax highlighter. Set to to let the highlighter control both + /// foreground and background colours. + /// + public bool UseThemeBackground + { + get; + set + { + if (field == value) + { + return; + } + + field = value; + ClearVisualLineCaches (); + SetNeedsDraw (); + } + } = true; + /// Whether tab characters render with a visible glyph in their first cell. public bool ShowTabs { @@ -731,7 +753,8 @@ private CellVisualLine BuildVisualLine ( styledSegments, selectionStart, selectionEnd, - LineTransformers); + LineTransformers, + UseThemeBackground); return _visualLineBuilder.Build (line, context); } diff --git a/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs b/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs index 31e29ae7..ae376a56 100644 --- a/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs +++ b/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs @@ -14,7 +14,8 @@ public sealed class VisualLineBuildContext ( IReadOnlyList? styledSegments, int selectionStart, int selectionEnd, - IEnumerable lineTransformers) + IEnumerable lineTransformers, + bool useThemeBackground = true) { public TextDocument Document { get; } = document; @@ -34,5 +35,11 @@ public sealed class VisualLineBuildContext ( public IEnumerable LineTransformers { get; } = lineTransformers; + /// + /// When , styled-segment backgrounds are replaced with + /// 's background so text blends into the theme. + /// + public bool UseThemeBackground { get; } = useThemeBackground; + public bool HasSelection => SelectionStart < SelectionEnd; } diff --git a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs index df1eae41..8a258212 100644 --- a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs +++ b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs @@ -157,8 +157,15 @@ private static Attribute GetAttribute (VisualLineBuildContext context, int segme return context.NormalAttribute; } - return context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute - ?? context.NormalAttribute; + Attribute segAttr = context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute + ?? context.NormalAttribute; + + if (context.UseThemeBackground) + { + return new Attribute (segAttr.Foreground, context.NormalAttribute.Background); + } + + return segAttr; } private static int GetSegmentEnd (IReadOnlyList? segments, int segmentIndex) diff --git a/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs b/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs index 4d0a09d4..0cd35a7a 100644 --- a/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs +++ b/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs @@ -193,6 +193,7 @@ public async Task Syntax_Highlighting_Uses_TextMate_Token_Attributes () #pragma warning disable CS0618 // Type or member is obsolete fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter (); #pragma warning restore CS0618 // Type or member is obsolete + fx.Top.Editor.UseThemeBackground = false; fx.Render (); TextMateSyntaxHighlighter highlighter = new (); @@ -328,4 +329,57 @@ public async Task Cursor_Position_After_Tab_Uses_Expanded_Tab_Columns () Assert.Equal (new Point (4, 0), fx.Top.Editor.Cursor.Position); } + + [Fact] + public async Task UseThemeBackground_True_Overrides_Highlighter_Background () + { + const string text = "public class C"; + + await using AppFixture fx = new (() => new EditorTestHost (text)); +#pragma warning disable CS0618 // Type or member is obsolete + fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter (); +#pragma warning restore CS0618 // Type or member is obsolete + fx.Top.Editor.UseThemeBackground = true; + fx.Render (); + + Attribute normal = fx.Top.Editor.GetAttributeForRole (VisualRole.Normal); + Cell cell = fx.Driver.Contents![0, 0]; + Assert.Equal ("p", cell.Grapheme); + + // The foreground should come from the highlighter (different from Normal's foreground). + TextMateSyntaxHighlighter highlighter = new (); + Attribute highlighterAttr = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; + Assert.Equal (highlighterAttr.Foreground, cell.Attribute!.Value.Foreground); + + // The background must match the theme's Normal background, not the highlighter's. + Assert.Equal (normal.Background, cell.Attribute!.Value.Background); + } + + [Fact] + public async Task UseThemeBackground_False_Preserves_Highlighter_Background () + { + const string text = "public class C"; + + await using AppFixture fx = new (() => new EditorTestHost (text)); +#pragma warning disable CS0618 // Type or member is obsolete + fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter (); +#pragma warning restore CS0618 // Type or member is obsolete + fx.Top.Editor.UseThemeBackground = false; + fx.Render (); + + TextMateSyntaxHighlighter highlighter = new (); + Attribute expected = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; + + Cell cell = fx.Driver.Contents![0, 0]; + Assert.Equal ("p", cell.Grapheme); + Assert.Equal (expected, cell.Attribute); + } + + [Fact] + public async Task UseThemeBackground_Defaults_To_True () + { + await using AppFixture fx = new (() => new EditorTestHost ("Hello")); + + Assert.True (fx.Top.Editor.UseThemeBackground); + } } From 3d4ee7256685af21e1b87f221d3e5904709cfb07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 13:52:31 +0000 Subject: [PATCH 3/6] style: rename abbreviated variable segAttr to segmentAttribute Agent-Logs-Url: https://github.com/gui-cs/Text/sessions/30aff306-af60-4170-890c-2cde2267ebef Co-authored-by: tig <585482+tig@users.noreply.github.com> --- src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs index 8a258212..05d4d4f1 100644 --- a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs +++ b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs @@ -157,15 +157,15 @@ private static Attribute GetAttribute (VisualLineBuildContext context, int segme return context.NormalAttribute; } - Attribute segAttr = context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute - ?? context.NormalAttribute; + Attribute segmentAttribute = context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute + ?? context.NormalAttribute; if (context.UseThemeBackground) { - return new Attribute (segAttr.Foreground, context.NormalAttribute.Background); + return new Attribute (segmentAttribute.Foreground, context.NormalAttribute.Background); } - return segAttr; + return segmentAttribute; } private static int GetSegmentEnd (IReadOnlyList? segments, int segmentIndex) From b0373155487274aeaf5307abb65f24b70ed16215 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 13:56:21 +0000 Subject: [PATCH 4/6] feat: add Use Theme Background option to ted's Options menu Adds a checked checkbox in Options > Use Theme Background that toggles Editor.UseThemeBackground. Default is checked (true). Agent-Logs-Url: https://github.com/gui-cs/Text/sessions/04a93e8d-10c9-429a-888b-170fdd5fa584 Co-authored-by: tig <585482+tig@users.noreply.github.com> --- examples/ted/TedApp.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/ted/TedApp.cs b/examples/ted/TedApp.cs index d48d40a3..ff6176ea 100644 --- a/examples/ted/TedApp.cs +++ b/examples/ted/TedApp.cs @@ -74,6 +74,19 @@ public TedApp (bool readOnly = false) Editor.ConvertTabsToSpaces = e.NewValue == CheckState.Checked; }; + CheckBox useThemeBackgroundCheckBox = new () + { + AllowCheckStateNone = false, + CanFocus = false, + Text = "Use _Theme Background", + Value = Editor.UseThemeBackground ? CheckState.Checked : CheckState.UnChecked + }; + + useThemeBackgroundCheckBox.ValueChanged += (_, e) => + { + Editor.UseThemeBackground = e.NewValue == CheckState.Checked; + }; + ThemeDropDown = new DropDownList { Value = ThemeName.DarkPlus, @@ -191,6 +204,11 @@ public TedApp (bool readOnly = false) { CommandView = convertTabsToSpacesCheckBox, HelpText = "Insert spaces when Tab is pressed" + }, + new MenuItem + { + CommandView = useThemeBackgroundCheckBox, + HelpText = "Use theme background for highlighted text" } ]), new MenuBarItem (Strings.menuHelp, From 6715c7acd6aed2f9f28008185d9ad6db35057dd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 14:16:25 +0000 Subject: [PATCH 5/6] fix: invert UseThemeBackground logic to match TG Markdown convention UseThemeBackground=true now means "use the syntax highlighting theme's background" (e.g. Dark Plus #1E1E1E), matching Terminal.Gui's Markdown view convention. When true, the viewport is also filled with the highlighter's DefaultBackground so empty cells match. UseThemeBackground=false overrides the highlighter's background with the TG scheme's Normal background. Also switches ted's menu item to the Action pattern (matching lineNumbersCheckBox) for reliable toggle behavior. Agent-Logs-Url: https://github.com/gui-cs/Text/sessions/cef8e227-1d3b-4069-8f43-f532a295fc69 Co-authored-by: tig <585482+tig@users.noreply.github.com> --- examples/ted/TedApp.cs | 9 +++-- src/Terminal.Gui.Editor/Editor.Drawing.cs | 36 +++++++++++++++++++ src/Terminal.Gui.Editor/Editor.cs | 9 ++--- .../Rendering/VisualLineBuildContext.cs | 5 +-- .../Rendering/VisualLineBuilder.cs | 2 +- .../EditorRenderingTests.cs | 31 ++++++++-------- 6 files changed, 64 insertions(+), 28 deletions(-) diff --git a/examples/ted/TedApp.cs b/examples/ted/TedApp.cs index ff6176ea..3d77a4d4 100644 --- a/examples/ted/TedApp.cs +++ b/examples/ted/TedApp.cs @@ -82,11 +82,6 @@ public TedApp (bool readOnly = false) Value = Editor.UseThemeBackground ? CheckState.Checked : CheckState.UnChecked }; - useThemeBackgroundCheckBox.ValueChanged += (_, e) => - { - Editor.UseThemeBackground = e.NewValue == CheckState.Checked; - }; - ThemeDropDown = new DropDownList { Value = ThemeName.DarkPlus, @@ -207,6 +202,10 @@ public TedApp (bool readOnly = false) }, new MenuItem { + Action = () => + { + Editor.UseThemeBackground = useThemeBackgroundCheckBox.Value == CheckState.Checked; + }, CommandView = useThemeBackgroundCheckBox, HelpText = "Use theme background for highlighted text" } diff --git a/src/Terminal.Gui.Editor/Editor.Drawing.cs b/src/Terminal.Gui.Editor/Editor.Drawing.cs index d27c4ddb..f2d911ca 100644 --- a/src/Terminal.Gui.Editor/Editor.Drawing.cs +++ b/src/Terminal.Gui.Editor/Editor.Drawing.cs @@ -22,6 +22,7 @@ protected override bool OnDrawingContent (DrawContext? context) Attribute normal = GetAttributeForRole (VisualRole.Normal); Attribute selected = GetAttributeForRole (VisualRole.Active); + FillViewportBackground (viewport, normal); DrawVisibleLines (viewport, normal, selected); SetAttribute (normal); UpdateCursor (); @@ -29,6 +30,41 @@ protected override bool OnDrawingContent (DrawContext? context) return true; } + /// + /// When is and a syntax highlighter + /// provides a , fills the viewport with + /// that background so empty cells match per-token backgrounds. + /// + private void FillViewportBackground (Rectangle viewport, Attribute normal) + { + if (!UseThemeBackground) + { + return; + } + +#pragma warning disable CS0618 // Type or member is obsolete + Terminal.Gui.Drawing.Color? themeBg = SyntaxHighlighter?.DefaultBackground; +#pragma warning restore CS0618 // Type or member is obsolete + + if (themeBg is not { } bg) + { + return; + } + + Attribute fillAttr = new (normal.Foreground, bg); + SetAttribute (fillAttr); + + for (var row = 0; row < viewport.Height; row++) + { + Move (0, row); + + for (var col = 0; col < viewport.Width; col++) + { + AddRune (' '); + } + } + } + private void DrawVisibleLines (Rectangle viewport, Attribute normal, Attribute selected) { // The CS0618 here is the API's purpose: SyntaxHighlighter is [Obsolete] to warn diff --git a/src/Terminal.Gui.Editor/Editor.cs b/src/Terminal.Gui.Editor/Editor.cs index 33114a23..624a2001 100644 --- a/src/Terminal.Gui.Editor/Editor.cs +++ b/src/Terminal.Gui.Editor/Editor.cs @@ -232,10 +232,11 @@ public int IndentationSize public bool ConvertTabsToSpaces { get; set; } /// - /// When (the default), syntax-highlighted tokens use the base - /// background instead of the background supplied by the - /// syntax highlighter. Set to to let the highlighter control both - /// foreground and background colours. + /// When (the default), syntax-highlighted tokens keep both their + /// foreground and background from the syntax highlighting theme (e.g. Dark Plus's + /// #1E1E1E background). Set to to override the + /// highlighter's background with the TG scheme's + /// background, matching the active application theme. /// public bool UseThemeBackground { diff --git a/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs b/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs index ae376a56..01cfac2f 100644 --- a/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs +++ b/src/Terminal.Gui.Editor/Rendering/VisualLineBuildContext.cs @@ -36,8 +36,9 @@ public sealed class VisualLineBuildContext ( public IEnumerable LineTransformers { get; } = lineTransformers; /// - /// When , styled-segment backgrounds are replaced with - /// 's background so text blends into the theme. + /// When , styled-segment backgrounds are preserved from the + /// syntax highlighting theme. When , backgrounds are replaced + /// with 's background so text blends into the TG scheme. /// public bool UseThemeBackground { get; } = useThemeBackground; diff --git a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs index 05d4d4f1..0feed13d 100644 --- a/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs +++ b/src/Terminal.Gui.Editor/Rendering/VisualLineBuilder.cs @@ -160,7 +160,7 @@ private static Attribute GetAttribute (VisualLineBuildContext context, int segme Attribute segmentAttribute = context.StyledSegments[Math.Min (segmentIndex, context.StyledSegments.Count - 1)].Attribute ?? context.NormalAttribute; - if (context.UseThemeBackground) + if (!context.UseThemeBackground) { return new Attribute (segmentAttribute.Foreground, context.NormalAttribute.Background); } diff --git a/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs b/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs index 0cd35a7a..052876b8 100644 --- a/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs +++ b/tests/Terminal.Gui.Editor.IntegrationTests/EditorRenderingTests.cs @@ -193,7 +193,6 @@ public async Task Syntax_Highlighting_Uses_TextMate_Token_Attributes () #pragma warning disable CS0618 // Type or member is obsolete fx.Top.Editor.SyntaxHighlighter = new TextMateSyntaxHighlighter (); #pragma warning restore CS0618 // Type or member is obsolete - fx.Top.Editor.UseThemeBackground = false; fx.Render (); TextMateSyntaxHighlighter highlighter = new (); @@ -331,7 +330,7 @@ public async Task Cursor_Position_After_Tab_Uses_Expanded_Tab_Columns () } [Fact] - public async Task UseThemeBackground_True_Overrides_Highlighter_Background () + public async Task UseThemeBackground_True_Preserves_Highlighter_Background () { const string text = "public class C"; @@ -342,21 +341,16 @@ public async Task UseThemeBackground_True_Overrides_Highlighter_Background () fx.Top.Editor.UseThemeBackground = true; fx.Render (); - Attribute normal = fx.Top.Editor.GetAttributeForRole (VisualRole.Normal); - Cell cell = fx.Driver.Contents![0, 0]; - Assert.Equal ("p", cell.Grapheme); - - // The foreground should come from the highlighter (different from Normal's foreground). TextMateSyntaxHighlighter highlighter = new (); - Attribute highlighterAttr = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; - Assert.Equal (highlighterAttr.Foreground, cell.Attribute!.Value.Foreground); + Attribute expected = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; - // The background must match the theme's Normal background, not the highlighter's. - Assert.Equal (normal.Background, cell.Attribute!.Value.Background); + Cell cell = fx.Driver.Contents![0, 0]; + Assert.Equal ("p", cell.Grapheme); + Assert.Equal (expected, cell.Attribute); } [Fact] - public async Task UseThemeBackground_False_Preserves_Highlighter_Background () + public async Task UseThemeBackground_False_Overrides_Highlighter_Background () { const string text = "public class C"; @@ -367,12 +361,17 @@ public async Task UseThemeBackground_False_Preserves_Highlighter_Background () fx.Top.Editor.UseThemeBackground = false; fx.Render (); - TextMateSyntaxHighlighter highlighter = new (); - Attribute expected = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; - + Attribute normal = fx.Top.Editor.GetAttributeForRole (VisualRole.Normal); Cell cell = fx.Driver.Contents![0, 0]; Assert.Equal ("p", cell.Grapheme); - Assert.Equal (expected, cell.Attribute); + + // The foreground should come from the highlighter (different from Normal's foreground). + TextMateSyntaxHighlighter highlighter = new (); + Attribute highlighterAttr = highlighter.Highlight (text, "csharp")[0].Attribute!.Value; + Assert.Equal (highlighterAttr.Foreground, cell.Attribute!.Value.Foreground); + + // The background must match the TG theme's Normal background, not the highlighter's. + Assert.Equal (normal.Background, cell.Attribute!.Value.Background); } [Fact] From 44752e30be952cf3db7b3412bc8502829f1bd418 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 14:18:48 +0000 Subject: [PATCH 6/6] perf: use AddStr for viewport background fill instead of per-cell AddRune Agent-Logs-Url: https://github.com/gui-cs/Text/sessions/cef8e227-1d3b-4069-8f43-f532a295fc69 Co-authored-by: tig <585482+tig@users.noreply.github.com> --- src/Terminal.Gui.Editor/Editor.Drawing.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Terminal.Gui.Editor/Editor.Drawing.cs b/src/Terminal.Gui.Editor/Editor.Drawing.cs index f2d911ca..174c9d62 100644 --- a/src/Terminal.Gui.Editor/Editor.Drawing.cs +++ b/src/Terminal.Gui.Editor/Editor.Drawing.cs @@ -54,14 +54,11 @@ private void FillViewportBackground (Rectangle viewport, Attribute normal) Attribute fillAttr = new (normal.Foreground, bg); SetAttribute (fillAttr); + var spaces = new string (' ', viewport.Width); + for (var row = 0; row < viewport.Height; row++) { - Move (0, row); - - for (var col = 0; col < viewport.Width; col++) - { - AddRune (' '); - } + AddStr (0, row, spaces); } }