From b5a59c03fe49f29072d5550c07eb033656183628 Mon Sep 17 00:00:00 2001 From: "Dan Thompson (SBS)" Date: Sat, 18 Jun 2022 08:56:01 -0700 Subject: [PATCH 01/13] Implement EnableColorSelection As described in #9583, this change implements the legacy conhost "EnableColorSelection" feature. @zadjii-msft was super nice and provided the outline/plumbing (WinRT classes and such) as a hackathon-type project (thank you!)--a "SelectionColor" runtimeclass, a ColorSelection method on the ControlCore runtimeclass, associated plumbing through the layers; plus the action-and-args plumbing to allow hooking up a basic "ColorSelection" action, which allows you to put actions in your settings JSON like so: ```json { "command": { "action": "experimental.colorSelection", "foreground": "#0f3" }, "keys": "alt+4" }, ``` On top of that foundation, I added a couple of things: * The ability to specify indexes for colors, in addition to RGB and RRGGBB colors. - It's a bit hacky, because there are some conversions that fight against sneaking an "I'm an index" flag in the alpha channel. * A new "matchMode" parameter on the action, allowing you to say if you want to only color the current selection ("0") or all matches ("1"). - I made it an int, because I'd like to enable at least one other "match mode" later, but it requires me/someone to fix up search.cpp to handle regex first. - Search used an old UIA "ColorSelection" method which was previously `E_NOTIMPL`, but is now wired up. Don't know what/if anything else uses this. * An uber-toggle setting, "EnableColorSelection", which allows you to set a single `bool` in your settings JSON, to light up all the keybindings you would expect from the legacy "EnableColorSelection" feature: - alt+[0..9]: color foreground - alt+shift+[0..9]: color foreground, all matches - ctrl+[0..9]: color background - ctrl+shift+[0..9]: color background, all matches * A few of the actions cannot be properly invoked via their keybindings, due to #13124. `*!*` But they work if you do them from the command palette. * If you have "`EnableColorSelection : true`" in your settings JSON, but then specify a different action in your JSON that uses the same key binding as a color selection keybinding, your custom one wins, which I think is the right thing. * I fixed what I think was a bug in search.cpp, which also affected the legacy EnableColorSelection feature: due to a non-inclusive coordinate comparison, you were not allowed to color a single character; but I see no reason why that should be disallowed. Now you can make all your `X`s red if you like. "Soft" spots: * I was a bit surprised at some of the helpers I had to provide in textBuffer.cpp. Perhaps there are existing methods that I didn't find? --- .github/actions/spelling/allow/apis.txt | 1 + doc/cascadia/profiles.schema.json | 42 ++ src/buffer/out/LineRendition.hpp | 7 + src/buffer/out/search.cpp | 13 +- src/buffer/out/textBuffer.cpp | 139 ++++++- src/buffer/out/textBuffer.hpp | 7 + .../TerminalApp/AppActionHandlers.cpp | 15 + src/cascadia/TerminalControl/ControlCore.cpp | 37 ++ src/cascadia/TerminalControl/ControlCore.h | 29 ++ src/cascadia/TerminalControl/ControlCore.idl | 9 + src/cascadia/TerminalControl/TermControl.cpp | 4 + src/cascadia/TerminalControl/TermControl.h | 2 + src/cascadia/TerminalControl/TermControl.idl | 2 + src/cascadia/TerminalCore/Terminal.cpp | 31 ++ src/cascadia/TerminalCore/Terminal.hpp | 3 + .../TerminalCore/TerminalSelection.cpp | 36 +- .../TerminalSettingsModel/ActionAndArgs.cpp | 2 + .../TerminalSettingsModel/ActionArgs.cpp | 167 ++++++++ .../TerminalSettingsModel/ActionArgs.h | 28 ++ .../TerminalSettingsModel/ActionArgs.idl | 7 + .../TerminalSettingsModel/ActionMap.cpp | 6 - .../AllShortcutActions.h | 152 +++---- .../CascadiaSettingsSerialization.cpp | 11 + .../GlobalAppSettings.idl | 1 + .../TerminalSettingsModel/MTSMSettings.h | 3 +- ...crosoft.Terminal.Settings.ModelLib.vcxproj | 4 + .../Resources/en-US/Resources.resw | 88 +++++ .../TerminalSettingsSerializationHelpers.h | 67 ++++ .../enableColorSelection.json | 370 ++++++++++++++++++ 29 files changed, 1185 insertions(+), 98 deletions(-) create mode 100644 src/cascadia/TerminalSettingsModel/enableColorSelection.json diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 4a8161c6aac..02d0d8d964d 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -92,6 +92,7 @@ istream IStringable ITab ITaskbar +itow IUri IVirtual KEYSELECT diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index a0e2b20507e..5c52a5f4def 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -376,6 +376,7 @@ "scrollToMark", "clearMark", "clearAllMarks", + "experimental.colorSelection", "unbound" ], "type": "string" @@ -836,6 +837,39 @@ } ] }, + "ColorSelectionAction": { + "description": "Arguments corresponding to a Color Selection Action", + "allOf": [ + { + "$ref": "#/$defs/ShortcutAction" + }, + { + "properties": { + "action": { + "type": "string", + "const": "experimental.colorSelection" + }, + "matchMode": { + "default": 0, + "description": "Specifies if only the selected text should be colored (0), or all instances of selected text (case-insensitive) (1).", + "maximum": 1, + "minimum": 0, + "type": "integer" + }, + "foreground": { + "type": "string", + "default": "", + "description": "The foreground color to use (either \"#RGB\" or \"#RRGGBB\" for an exact color, or \"iNN\" for an indexed color)." + }, + "background": { + "type": "string", + "default": "", + "description": "The background color to use (either \"#RGB\" or \"#RRGGBB\" for an exact color, or \"iNN\" for an indexed color)." + } + } + } + ] + }, "OpenSettingsAction": { "description": "Arguments corresponding to a Open Settings Action", "allOf": [ @@ -1611,6 +1645,9 @@ { "$ref": "#/$defs/AdjustOpacityAction" }, + { + "$ref": "#/$defs/ColorSelectionAction" + }, { "type": "null" } @@ -1733,6 +1770,11 @@ "description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.", "type": "boolean" }, + "experimental.enableColorSelection": { + "default": false, + "description": "When set to true, adds 40 preset \"Color Selection\" actions (keybindings) to allow colorizing selected text via keystroke, similar to the legacy conhost EnableColorSelection feature (such as alt+6 to color the selection red).", + "type": "boolean" + }, "disableAnimations": { "default": false, "description": "When set to `true`, visual animations will be disabled across the application.", diff --git a/src/buffer/out/LineRendition.hpp b/src/buffer/out/LineRendition.hpp index db7a5489ca7..7be4a7b59bf 100644 --- a/src/buffer/out/LineRendition.hpp +++ b/src/buffer/out/LineRendition.hpp @@ -28,6 +28,13 @@ constexpr til::inclusive_rect ScreenToBufferLine(const til::inclusive_rect& line return { line.Left >> scale, line.Top, line.Right >> scale, line.Bottom }; } +constexpr til::point ScreenToBufferLine(const til::point& line, const LineRendition lineRendition) +{ + // Use shift right to quickly divide the Left and Right by 2 for double width lines. + const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1; + return { line.X >> scale, line.Y }; +} + constexpr til::inclusive_rect BufferToScreenLine(const til::inclusive_rect& line, const LineRendition lineRendition) { // Use shift left to quickly multiply the Left and Right by 2 for double width lines. diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index 30731eec7cc..67b7359ac2d 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -106,17 +106,14 @@ void Search::Select() const } // Routine Description: -// - In console host, we take the found word and apply the given color to it in the screen buffer -// - In Windows Terminal, we just select the found word, but we do not modify the buffer +// - Applies the supplied TextAttribute to the current search result. // Arguments: -// - ulAttr - The legacy color attribute to apply to the word +// - attr - The attribute to apply to the result void Search::Color(const TextAttribute attr) const { - // Only select if we've found something. - if (_coordSelStart != _coordSelEnd) - { - _uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr); - } + // Note that _coordSelStart may be equal to _coordSelEnd (but it's an inclusive + // selection: if they are equal, it means we are applying to a single character). + _uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr); } // Routine Description: diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index ac699ec4351..b393a1adf8c 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1611,7 +1611,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional // - bufferCoordinates: when enabled, treat the coordinates as relative to // the buffer rather than the screen. // Return Value: -// - the delimiter class for the given char +// - One or more rects corresponding to the selection area const std::vector TextBuffer::GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const { std::vector textRects; @@ -1660,6 +1660,72 @@ const std::vector TextBuffer::GetTextRects(til::point start return textRects; } +// Method Description: +// - Computes the span(s) for the given selection +// - If not a blockSelection, returns a single span (start - end) +// - Else if a blockSelection, returns spans corresponding to each line in the block selection +// Arguments: +// - start: beginning of the text region of interest (inclusive) +// - end: the other end of the text region of interest (inclusive) +// - blockSelection: when enabled, get spans for each line covered by the block +// - bufferCoordinates: when enabled, treat the coordinates as relative to +// the buffer rather than the screen. +// Return Value: +// - one or more sets of start-end coordinates, representing spans of text in the buffer +const std::vector> TextBuffer::GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const +{ + std::vector> textSpans; + + if (blockSelection) + { + // If blockSelection, this is effectively the same operation as GetTextRects, but + // expressed in til::point coordinates. + auto rects = GetTextRects(start, end, /*blockSelection*/ true, bufferCoordinates); + textSpans.reserve(rects.size()); + + for (auto rect : rects) + { + til::point first = { rect.Left, rect.Top }; + til::point second = { rect.Right, rect.Bottom }; + auto span = std::make_tuple(first, second); + textSpans.emplace_back(span); + } + } + else + { + const auto bufferSize = GetSize(); + + // (0,0) is the top-left of the screen + // the physically "higher" coordinate is closer to the top-left + // the physically "lower" coordinate is closer to the bottom-right + auto [higherCoord, lowerCoord] = start <= end ? + std::make_tuple(start, end) : + std::make_tuple(end, start); + + textSpans.reserve(1); + + // If we were passed screen coordinates, convert the given range into + // equivalent buffer offsets, taking line rendition into account. + if (!bufferCoordinates) + { + higherCoord = ScreenToBufferLine(higherCoord, GetLineRendition(higherCoord.Y)); + lowerCoord = ScreenToBufferLine(lowerCoord, GetLineRendition(lowerCoord.Y)); + } + + til::inclusive_rect asRect = { higherCoord.X, higherCoord.Y, lowerCoord.X, lowerCoord.Y }; + _ExpandTextRow(asRect); + higherCoord.X = asRect.Left; + higherCoord.Y = asRect.Top; + lowerCoord.X = asRect.Right; + lowerCoord.Y = asRect.Bottom; + + auto span = std::make_tuple(higherCoord, lowerCoord); + textSpans.emplace_back(span); + } + + return textSpans; +} + // Method Description: // - Expand the selection row according to include wide glyphs fully // - this is particularly useful for box selections (ALT + selection) @@ -1832,6 +1898,77 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF, return data; } +size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coordEnd) const +{ + assert((coordEnd.Y > coordStart.Y) || + ((coordEnd.Y == coordStart.Y) && (coordEnd.X >= coordStart.X))); + + // Note that this could also be computed using CompareInBounds, but that function + // seems disfavored lately. + // + // CompareInBounds version: + // + // const auto bufferSize = GetSize(); + // // Note that we negate because CompareInBounds is backwards from what we are trying to calculate. + // auto length = - bufferSize.CompareInBounds(coordStart, coordEnd); + // length += 1; // because we need "inclusive" behavior. + + const auto rowSize = gsl::narrow(GetRowByOffset(0).size()); + + size_t length = (static_cast(coordEnd.Y) - coordStart.Y) * rowSize; + length += (static_cast(coordEnd.X) - coordStart.X) + 1; // "+1" is because we need "inclusive" behavior + + return length; +} + +// Routine Description: +// - Retrieves the plain text data between the specified coordinates. +// Arguments: +// - trimTrailingWhitespace - remove the trailing whitespace at the end of the result. +// - start - where to start getting text (should be at or prior to "end") +// - end - where to end getting text +// Return Value: +// - Just the text. +const std::wstring TextBuffer::GetPlainText(const bool trimTrailingWhitespace, + const til::point& start, + const til::point& end) const +{ + std::wstring text; + // TODO: should I put in protections for start coming before end? + auto spanLength = SpanLength(start, end); + text.reserve(spanLength); + + auto it = GetCellDataAt(start); + + // copy char data into the string buffer, skipping trailing bytes + // TODO: is using spanLength like this the right way to do it? + while (it && ((spanLength) > 0)) + { + const auto& cell = *it; + spanLength--; + + if (!cell.DbcsAttr().IsTrailing()) + { + const auto chars = cell.Chars(); + text.append(chars); + } +#pragma warning(suppress : 26444) + // TODO GH 2675: figure out why there's custom construction/destruction happening here + it++; + } + + if (trimTrailingWhitespace) + { + // remove the spaces at the end (aka trim the trailing whitespace) + while (!text.empty() && text.back() == UNICODE_SPACE) + { + text.pop_back(); + } + } + + return text; +} + // Routine Description: // - Generates a CF_HTML compliant structure based on the passed in text and color data // Arguments: diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 61f82599acd..c0cc35681c1 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -166,6 +166,7 @@ class TextBuffer final bool MoveToPreviousGlyph(til::point& pos, std::optional limitOptional = std::nullopt) const; const std::vector GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const; + const std::vector> GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const; void AddHyperlinkToMap(std::wstring_view uri, uint16_t id); std::wstring GetHyperlinkUriFromId(uint16_t id) const; @@ -182,12 +183,18 @@ class TextBuffer final std::vector> BkAttr; }; + size_t SpanLength(const til::point coordStart, const til::point coordEnd) const; + const TextAndColor GetText(const bool includeCRLF, const bool trimTrailingWhitespace, const std::vector& textRects, std::function(const TextAttribute&)> GetAttributeColors = nullptr, const bool formatWrappedRows = false) const; + const std::wstring GetPlainText(const bool trimTrailingWhitespace, + const til::point& start, + const til::point& end) const; + static std::string GenHTML(const TextAndColor& rows, const int fontHeightPoints, const std::wstring_view fontFaceName, diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 10a7f8cfa31..8a628c85dfe 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1122,4 +1122,19 @@ namespace winrt::TerminalApp::implementation args.Handled(handled); } } + + void TerminalPage::_HandleColorSelection(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + if (args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + const auto res = _ApplyToActiveControls([&](auto& control) { + control.ColorSelection(realArgs.Foreground(), realArgs.Background(), realArgs.MatchMode()); + }); + args.Handled(res); + } + } + } } diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 2ed9f2d7efe..74ee5d7614b 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -16,6 +16,7 @@ #include "../../renderer/atlas/AtlasEngine.h" #include "../../renderer/dx/DxRenderer.hpp" +#include "SelectionColor.g.cpp" #include "ControlCore.g.cpp" using namespace ::Microsoft::Console::Types; @@ -2136,4 +2137,40 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } } + + void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode) + { + if (HasSelection()) + { + const auto pForeground = winrt::get_self(fg); + const auto pBackground = winrt::get_self(bg); + + TextColor foregroundAsTextColor; + TextColor backgroundAsTextColor; + + if (pForeground) + { + foregroundAsTextColor = pForeground->Color(); + } + + if (pBackground) + { + backgroundAsTextColor = pBackground->Color(); + } + + TextAttribute attr; + attr.SetForeground(foregroundAsTextColor); + attr.SetBackground(backgroundAsTextColor); + + _terminal->ColorSelection(attr, matchMode); + _terminal->ClearSelection(); + if (matchMode > 0) + { + // ClearSelection will invalidate the selection area... but if we are + // coloring other matches, then we need to make sure those get redrawn, + // too. + _renderer->TriggerRedrawAll(); + } + } + } } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index c8ba9b88d7d..2ed80cf7653 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -15,12 +15,14 @@ #pragma once +#include "SelectionColor.g.h" #include "ControlCore.g.h" #include "ControlSettings.h" #include "../../audio/midi/MidiAudio.hpp" #include "../../renderer/base/Renderer.hpp" #include "../../cascadia/TerminalCore/Terminal.hpp" #include "../buffer/out/search.h" +#include "../buffer/out/TextColor.h" #include @@ -40,6 +42,30 @@ public: \ namespace winrt::Microsoft::Terminal::Control::implementation { + struct SelectionColor : SelectionColorT + { + SelectionColor() = default; + WINRT_PROPERTY(uint32_t, TextColor); + + public: + ::TextColor Color() const + { + ::TextColor asTextColor; + + // High bits set indicate an indexed color. + if (_TextColor & 0xff000000) + { + asTextColor.SetIndex(_TextColor & 0xff, false); + } + else + { + asTextColor.SetColor(_TextColor); + } + + return asTextColor; + }; + }; + struct ControlCore : ControlCoreT { public: @@ -104,6 +130,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Microsoft::Console::Types::IUiaData* GetUiaData() const; + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode); + void Close(); #pragma region ICoreState @@ -337,5 +365,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::factory_implementation { + BASIC_FACTORY(SelectionColor); BASIC_FACTORY(ControlCore); } diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index ec8230fa180..a3b4bca2fe4 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -53,6 +53,13 @@ namespace Microsoft.Terminal.Control Boolean EndAtRightBoundary; }; + [default_interface] runtimeclass SelectionColor + { + SelectionColor(); + // Will be converted to a TextColor (from buffer/out/TextColor.h) + UInt32 TextColor; + } + [default_interface] runtimeclass ControlCore : ICoreState { ControlCore(IControlSettings settings, @@ -132,6 +139,8 @@ namespace Microsoft.Terminal.Control void AdjustOpacity(Double Opacity, Boolean relative); void WindowVisibilityChanged(Boolean showOrHide); + void ColorSelection(SelectionColor fg, SelectionColor bg, UInt32 matchMode); + event FontSizeChangedEventArgs FontSizeChanged; event Windows.Foundation.TypedEventHandler CopyToClipboard; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 943190cfaf2..117f1aef4f0 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -3063,4 +3063,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ScrollMarks(); } + void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode) + { + _core.ColorSelection(fg, bg, matchMode); + } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 655fce03fdb..89446fae7da 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -48,6 +48,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void WindowVisibilityChanged(const bool showOrHide); + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode); + #pragma region ICoreState const uint64_t TaskbarState() const noexcept; const uint64_t TaskbarProgress() const noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 101359b30e9..0b989b150b1 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -92,5 +92,7 @@ namespace Microsoft.Terminal.Control Double BackgroundOpacity { get; }; Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; + + void ColorSelection(SelectionColor fg, SelectionColor bg, UInt32 matchMode); } } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 7482def5e32..c8a17550b09 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -8,6 +8,7 @@ #include "../../inc/unicode.hpp" #include "../../types/inc/utils.hpp" #include "../../types/inc/colorTable.hpp" +#include "../../buffer/out/search.h" #include @@ -1638,3 +1639,33 @@ til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal:: } } } + +void Terminal::ColorSelection(const TextAttribute& attr, uint32_t matchMode) +{ + for (const auto [start, end] : _GetSelectionSpans()) + { + try + { + if (matchMode == 0) + { + const auto length = _activeBuffer().SpanLength(start, end); + _activeBuffer().Write(OutputCellIterator(attr, length), start); + } + else if (matchMode == 1) + { + const auto text = _activeBuffer().GetPlainText(/*trimTrailingWhitespace*/ IsBlockSelection(), start, end); + + if (!text.empty()) + { + Search search(*this, text, Search::Direction::Forward, Search::Sensitivity::CaseInsensitive, { 0, 0 }); + + while (search.FindNext()) + { + search.Color(attr); + } + } + } + } + CATCH_LOG(); + } +} diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 5dec2e85124..e10e244fa16 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -232,6 +232,8 @@ class Microsoft::Terminal::Core::Terminal final : const size_t GetTaskbarState() const noexcept; const size_t GetTaskbarProgress() const noexcept; + void ColorSelection(const TextAttribute& attr, uint32_t matchMode); + #pragma region TextSelection // These methods are defined in TerminalSelection.cpp enum class SelectionInteractionMode @@ -431,6 +433,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; + std::vector> _GetSelectionSpans() const noexcept; std::pair _PivotSelection(const til::point targetPos, bool& targetStart) const; std::pair _ExpandSelectionAnchors(std::pair anchors) const; til::point _ConvertToBufferCell(const til::point viewportPos) const; diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index a237f68dfcd..257ecfd3ef5 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -63,6 +63,27 @@ std::vector Terminal::_GetSelectionRects() const noexcept return result; } +// Method Description: +// - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection. +// Return Value: +// - A vector of one or more spans representing the selection. They are absolute coordinates relative to the buffer origin. +std::vector> Terminal::_GetSelectionSpans() const noexcept +{ + std::vector> result; + + if (!IsSelectionActive()) + { + return result; + } + + try + { + return _activeBuffer().GetTextSpans(_selection->start, _selection->end, _blockSelection, false); + } + CATCH_LOG(); + return result; +} + // Method Description: // - Get the current anchor position relative to the whole text buffer // Arguments: @@ -841,13 +862,14 @@ void Terminal::_ScrollToPoint(const til::point pos) } // Method Description: -// - This method won't be used. We just throw and do nothing. For now we -// need this method to implement UiaData interface +// - apply the TextAttribute "attr" to the active buffer // Arguments: -// - coordSelectionStart - Not used -// - coordSelectionEnd - Not used -// - attr - Not used. -void Terminal::ColorSelection(const til::point, const til::point, const TextAttribute) +// - coordStart - where to begin applying attr +// - coordEnd - where to end applying attr (inclusive) +// - attr - the text attributes to apply +void Terminal::ColorSelection(const til::point coordStart, const til::point coordEnd, const TextAttribute attr) { - THROW_HR(E_NOTIMPL); + size_t spanLength = _activeBuffer().SpanLength(coordStart, coordEnd); + + _activeBuffer().Write(OutputCellIterator(attr, spanLength), coordStart); } diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 8591457e611..b31ce0ba8c3 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -83,6 +83,7 @@ static constexpr std::string_view SelectAllKey{ "selectAll" }; static constexpr std::string_view MarkModeKey{ "markMode" }; static constexpr std::string_view ToggleBlockSelectionKey{ "toggleBlockSelection" }; static constexpr std::string_view SwitchSelectionEndpointKey{ "switchSelectionEndpoint" }; +static constexpr std::string_view ColorSelectionKey{ "experimental.colorSelection" }; static constexpr std::string_view ActionKey{ "action" }; @@ -402,6 +403,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") }, { ShortcutAction::ToggleBlockSelection, RS_(L"ToggleBlockSelectionCommandKey") }, { ShortcutAction::SwitchSelectionEndpoint, RS_(L"SwitchSelectionEndpointCommandKey") }, + { ShortcutAction::ColorSelection, L"" }, // Intentionally omitted, must be generated by GenerateName }; }(); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 3b3c56543a1..947389f8ae5 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -43,6 +43,7 @@ #include "ClearBufferArgs.g.cpp" #include "MultipleActionsArgs.g.cpp" #include "AdjustOpacityArgs.g.cpp" +#include "ColorSelectionArgs.g.cpp" #include #include @@ -840,4 +841,170 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation }; } } + + winrt::hstring _FormatColorString(uint32_t color) + { + winrt::hstring colorStr; + + if ((color & 0x01000000) == 0x01000000) + { + // It's an indexed color. + uint8_t idx = color & 0x000000ff; + + switch (idx) + { + case 0: + colorStr = RS_(L"ColorSelection_Black"); // "black" + break; + + case 1: + // AKA "dark red" + colorStr = RS_(L"ColorSelection_Red"); // "red" + break; + + case 2: + // AKA "dark green" + colorStr = RS_(L"ColorSelection_Green"); // "green" + break; + + case 3: + // AKA "dark yellow" + colorStr = RS_(L"ColorSelection_Yellow"); // "yellow" + break; + + case 4: + // AKA "dark blue" + colorStr = RS_(L"ColorSelection_Blue"); // "blue" + break; + + case 5: + // AKA "dark magenta" + colorStr = RS_(L"ColorSelection_Purple"); // "purple" + break; + + case 6: + // AK "dark cyan" + colorStr = RS_(L"ColorSelection_Cyan"); // "cyan" + break; + + case 7: + // AKA "gray" / "dark white" + colorStr = RS_(L"ColorSelection_White"); // "white" + break; + + case 8: + // AKA "dark gray" + colorStr = RS_(L"ColorSelection_BrightBlack"); // "bright black" + break; + + case 9: + // AKA "red" + colorStr = RS_(L"ColorSelection_BrightRed"); // "bright red" + break; + + case 10: + // AKA "green" + colorStr = RS_(L"ColorSelection_BrightGreen"); // "bright green" + break; + + case 11: + // AKA "yellow" + colorStr = RS_(L"ColorSelection_BrightYellow"); // "bright yellow" + break; + + case 12: + // AKA "blue" + colorStr = RS_(L"ColorSelection_BrightBlue"); // "bright blue" + break; + + case 13: + // AKA "magenta" + colorStr = RS_(L"ColorSelection_BrightPurple"); // "bright purple" + break; + + case 14: + // AKA "cyan" + colorStr = RS_(L"ColorSelection_BrightCyan"); // "bright cyan" + break; + + case 15: + // AKA "white" + colorStr = RS_(L"ColorSelection_BrightWhite"); // "bright white" + break; + + default: + wchar_t tempBuf[9] = { 0 }; + swprintf_s(tempBuf, L"i%02i", idx); + colorStr = tempBuf; + } + } + else + { + wchar_t tempBuf[9] = { 0 }; + auto err = _itow_s(color, tempBuf, 16); + assert(err == 0); + UNREFERENCED_PARAMETER(err); + colorStr = tempBuf; + } + + return colorStr; + } + + winrt::hstring ColorSelectionArgs::GenerateName() const + { + auto matchMode = MatchMode() ? MatchMode() : 0; + + auto matchModeStr = winrt::hstring{}; + if (matchMode) + { + if (matchMode == 1) + { + matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" + } + } + + bool hasForeground = (bool)Foreground(); + bool hasBackground = (bool)Background(); + + winrt::hstring fgStr = hasForeground ? _FormatColorString(Foreground().TextColor()) : RS_(L"ColorSelection_defaultColor"); // "[default]" + winrt::hstring bgStr = hasBackground ? _FormatColorString(Background().TextColor()) : RS_(L"ColorSelection_defaultColor"); // "[default]" + + // To try to keep things simple for the user, we'll try to show only the + // "interesting" color (i.e. leave off the bg or fg if it is either unspecified or + // black or index 0). + // + // Note that we mask off the alpha channel, which is used to indicate if it's an + // indexed color. + bool foregroundIsExplicitBlack = hasForeground && (Foreground().TextColor() & 0x00ffffff) == 0; + bool backgroundIsExplicitBlack = hasBackground && (Background().TextColor() & 0x00ffffff) == 0; + + if (hasForeground && (!hasBackground || backgroundIsExplicitBlack)) + { + auto str = RS_(L"ColorSelection_fg_action"); // "Color selection, foreground: {0}{1}" + return winrt::hstring{ + fmt::format(str.c_str(), fgStr, matchModeStr) + }; + } + else if (hasBackground && (!hasForeground || foregroundIsExplicitBlack)) + { + auto str = RS_(L"ColorSelection_bg_action"); // "Color selection, background: {0}{1}" + return winrt::hstring{ + fmt::format(str.c_str(), bgStr, matchModeStr) + }; + } + else if (hasForeground && hasBackground) + { + auto str = RS_(L"ColorSelection_fg_bg_action"); // "Color selection, foreground: {0}, background: {1}{2}" + return winrt::hstring{ + fmt::format(str.c_str(), fgStr, bgStr, matchModeStr) + }; + } + else + { + auto str = RS_(L"ColorSelection_default_action"); // "Color selection, (default foreground/background){0}" + return winrt::hstring{ + fmt::format(str.c_str(), matchModeStr) + }; + } + } } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 03f69252561..5bba7206a02 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -45,6 +45,7 @@ #include "ClearBufferArgs.g.h" #include "MultipleActionsArgs.g.h" #include "AdjustOpacityArgs.g.h" +#include "ColorSelectionArgs.g.h" #include "JsonUtils.h" #include "HashUtils.h" @@ -235,6 +236,12 @@ private: X(int32_t, Opacity, "opacity", false, 0) \ X(bool, Relative, "relative", false, true) +//////////////////////////////////////////////////////////////////////////////// +#define COLOR_SELECTION_ARGS(X) \ + X(winrt::Microsoft::Terminal::Control::SelectionColor, Foreground, "foreground", false, nullptr) \ + X(winrt::Microsoft::Terminal::Control::SelectionColor, Background, "background", false, nullptr) \ + X(uint32_t, MatchMode, "matchMode", false, 0u) + //////////////////////////////////////////////////////////////////////////////// namespace winrt::Microsoft::Terminal::Settings::Model::implementation @@ -384,6 +391,25 @@ struct til::hash_trait +struct til::hash_trait +{ + using M = winrt::Microsoft::Terminal::Control::SelectionColor; + + void operator()(hasher& h, const M& value) const noexcept + { + if (value) + { + h.write(value.TextColor()); + } + else + { + // N.B. it is important even for a non-value to contribute to the hash, else + // it is easier to have hash collisions. + h.write(-1); + } + } +}; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { @@ -713,6 +739,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ACTION_ARGS_STRUCT(AdjustOpacityArgs, ADJUST_OPACITY_ARGS); + ACTION_ARGS_STRUCT(ColorSelectionArgs, COLOR_SELECTION_ARGS); + } namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 77c817315b6..fa0c01bbbf2 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -377,4 +377,11 @@ namespace Microsoft.Terminal.Settings.Model Int32 Opacity { get; }; Boolean Relative { get; }; }; + + [default_interface] runtimeclass ColorSelectionArgs : IActionArgs + { + Microsoft.Terminal.Control.SelectionColor Foreground; + Microsoft.Terminal.Control.SelectionColor Background; + UInt32 MatchMode { get; }; + }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 9e7bb650ddf..5809369a660 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -196,7 +196,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // Update NameMap with our parents. // Starting with this means we're doing a top-down approach. - assert(_parents.size() <= 1); for (const auto& parent : _parents) { parent->_PopulateNameMapWithSpecialCommands(nameMap); @@ -274,7 +273,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation }); // Now, add the accumulated actions from our parents - assert(_parents.size() <= 1); for (const auto& parent : _parents) { const auto parentActions{ parent->_GetCumulativeActions() }; @@ -367,7 +365,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } // Update keyBindingsMap and unboundKeys with our parents - assert(_parents.size() <= 1); for (const auto& parent : _parents) { parent->_PopulateKeyBindingMapWithStandardCommands(keyBindingsMap, unboundKeys); @@ -408,7 +405,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation actionMap->_IterableCommands.emplace_back(*winrt::get_self(cmd)->Copy()); } - assert(_parents.size() <= 1); actionMap->_parents.reserve(_parents.size()); for (const auto& parent : _parents) { @@ -737,7 +733,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // the command was not bound in this layer, // ask my parents - assert(_parents.size() <= 1); for (const auto& parent : _parents) { const auto& inheritedCmd{ parent->_GetActionByKeyChordInternal(keys) }; @@ -787,7 +782,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } // Check our parents - assert(_parents.size() <= 1); for (const auto& parent : _parents) { if (const auto& keys{ parent->GetKeyBindingForAction(myAction, myArgs) }) diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index 23d4cf2110c..4409d5a8598 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -23,80 +23,81 @@ // each action. This is _NOT_ something that should be used when any individual // case should be customized. -#define ALL_SHORTCUT_ACTIONS \ - ON_ALL_ACTIONS(CopyText) \ - ON_ALL_ACTIONS(PasteText) \ - ON_ALL_ACTIONS(OpenNewTabDropdown) \ - ON_ALL_ACTIONS(DuplicateTab) \ - ON_ALL_ACTIONS(NewTab) \ - ON_ALL_ACTIONS(CloseWindow) \ - ON_ALL_ACTIONS(CloseTab) \ - ON_ALL_ACTIONS(ClosePane) \ - ON_ALL_ACTIONS(NextTab) \ - ON_ALL_ACTIONS(PrevTab) \ - ON_ALL_ACTIONS(SendInput) \ - ON_ALL_ACTIONS(SplitPane) \ - ON_ALL_ACTIONS(ToggleSplitOrientation) \ - ON_ALL_ACTIONS(TogglePaneZoom) \ - ON_ALL_ACTIONS(SwitchToTab) \ - ON_ALL_ACTIONS(AdjustFontSize) \ - ON_ALL_ACTIONS(ResetFontSize) \ - ON_ALL_ACTIONS(ScrollUp) \ - ON_ALL_ACTIONS(ScrollDown) \ - ON_ALL_ACTIONS(ScrollUpPage) \ - ON_ALL_ACTIONS(ScrollDownPage) \ - ON_ALL_ACTIONS(ScrollToTop) \ - ON_ALL_ACTIONS(ScrollToBottom) \ - ON_ALL_ACTIONS(ScrollToMark) \ - ON_ALL_ACTIONS(AddMark) \ - ON_ALL_ACTIONS(ClearMark) \ - ON_ALL_ACTIONS(ClearAllMarks) \ - ON_ALL_ACTIONS(ResizePane) \ - ON_ALL_ACTIONS(MoveFocus) \ - ON_ALL_ACTIONS(MovePane) \ - ON_ALL_ACTIONS(SwapPane) \ - ON_ALL_ACTIONS(Find) \ - ON_ALL_ACTIONS(ToggleShaderEffects) \ - ON_ALL_ACTIONS(ToggleFocusMode) \ - ON_ALL_ACTIONS(ToggleFullscreen) \ - ON_ALL_ACTIONS(ToggleAlwaysOnTop) \ - ON_ALL_ACTIONS(OpenSettings) \ - ON_ALL_ACTIONS(SetFocusMode) \ - ON_ALL_ACTIONS(SetFullScreen) \ - ON_ALL_ACTIONS(SetMaximized) \ - ON_ALL_ACTIONS(SetColorScheme) \ - ON_ALL_ACTIONS(SetTabColor) \ - ON_ALL_ACTIONS(OpenTabColorPicker) \ - ON_ALL_ACTIONS(RenameTab) \ - ON_ALL_ACTIONS(OpenTabRenamer) \ - ON_ALL_ACTIONS(ExecuteCommandline) \ - ON_ALL_ACTIONS(ToggleCommandPalette) \ - ON_ALL_ACTIONS(CloseOtherTabs) \ - ON_ALL_ACTIONS(CloseTabsAfter) \ - ON_ALL_ACTIONS(TabSearch) \ - ON_ALL_ACTIONS(MoveTab) \ - ON_ALL_ACTIONS(BreakIntoDebugger) \ - ON_ALL_ACTIONS(TogglePaneReadOnly) \ - ON_ALL_ACTIONS(FindMatch) \ - ON_ALL_ACTIONS(NewWindow) \ - ON_ALL_ACTIONS(IdentifyWindow) \ - ON_ALL_ACTIONS(IdentifyWindows) \ - ON_ALL_ACTIONS(RenameWindow) \ - ON_ALL_ACTIONS(OpenWindowRenamer) \ - ON_ALL_ACTIONS(GlobalSummon) \ - ON_ALL_ACTIONS(QuakeMode) \ - ON_ALL_ACTIONS(FocusPane) \ - ON_ALL_ACTIONS(OpenSystemMenu) \ - ON_ALL_ACTIONS(ExportBuffer) \ - ON_ALL_ACTIONS(ClearBuffer) \ - ON_ALL_ACTIONS(MultipleActions) \ - ON_ALL_ACTIONS(Quit) \ - ON_ALL_ACTIONS(AdjustOpacity) \ - ON_ALL_ACTIONS(RestoreLastClosed) \ - ON_ALL_ACTIONS(SelectAll) \ - ON_ALL_ACTIONS(MarkMode) \ - ON_ALL_ACTIONS(ToggleBlockSelection) \ - ON_ALL_ACTIONS(SwitchSelectionEndpoint) +#define ALL_SHORTCUT_ACTIONS \ + ON_ALL_ACTIONS(CopyText) \ + ON_ALL_ACTIONS(PasteText) \ + ON_ALL_ACTIONS(OpenNewTabDropdown) \ + ON_ALL_ACTIONS(DuplicateTab) \ + ON_ALL_ACTIONS(NewTab) \ + ON_ALL_ACTIONS(CloseWindow) \ + ON_ALL_ACTIONS(CloseTab) \ + ON_ALL_ACTIONS(ClosePane) \ + ON_ALL_ACTIONS(NextTab) \ + ON_ALL_ACTIONS(PrevTab) \ + ON_ALL_ACTIONS(SendInput) \ + ON_ALL_ACTIONS(SplitPane) \ + ON_ALL_ACTIONS(ToggleSplitOrientation) \ + ON_ALL_ACTIONS(TogglePaneZoom) \ + ON_ALL_ACTIONS(SwitchToTab) \ + ON_ALL_ACTIONS(AdjustFontSize) \ + ON_ALL_ACTIONS(ResetFontSize) \ + ON_ALL_ACTIONS(ScrollUp) \ + ON_ALL_ACTIONS(ScrollDown) \ + ON_ALL_ACTIONS(ScrollUpPage) \ + ON_ALL_ACTIONS(ScrollDownPage) \ + ON_ALL_ACTIONS(ScrollToTop) \ + ON_ALL_ACTIONS(ScrollToBottom) \ + ON_ALL_ACTIONS(ScrollToMark) \ + ON_ALL_ACTIONS(AddMark) \ + ON_ALL_ACTIONS(ClearMark) \ + ON_ALL_ACTIONS(ClearAllMarks) \ + ON_ALL_ACTIONS(ResizePane) \ + ON_ALL_ACTIONS(MoveFocus) \ + ON_ALL_ACTIONS(MovePane) \ + ON_ALL_ACTIONS(SwapPane) \ + ON_ALL_ACTIONS(Find) \ + ON_ALL_ACTIONS(ToggleShaderEffects) \ + ON_ALL_ACTIONS(ToggleFocusMode) \ + ON_ALL_ACTIONS(ToggleFullscreen) \ + ON_ALL_ACTIONS(ToggleAlwaysOnTop) \ + ON_ALL_ACTIONS(OpenSettings) \ + ON_ALL_ACTIONS(SetFocusMode) \ + ON_ALL_ACTIONS(SetFullScreen) \ + ON_ALL_ACTIONS(SetMaximized) \ + ON_ALL_ACTIONS(SetColorScheme) \ + ON_ALL_ACTIONS(SetTabColor) \ + ON_ALL_ACTIONS(OpenTabColorPicker) \ + ON_ALL_ACTIONS(RenameTab) \ + ON_ALL_ACTIONS(OpenTabRenamer) \ + ON_ALL_ACTIONS(ExecuteCommandline) \ + ON_ALL_ACTIONS(ToggleCommandPalette) \ + ON_ALL_ACTIONS(CloseOtherTabs) \ + ON_ALL_ACTIONS(CloseTabsAfter) \ + ON_ALL_ACTIONS(TabSearch) \ + ON_ALL_ACTIONS(MoveTab) \ + ON_ALL_ACTIONS(BreakIntoDebugger) \ + ON_ALL_ACTIONS(TogglePaneReadOnly) \ + ON_ALL_ACTIONS(FindMatch) \ + ON_ALL_ACTIONS(NewWindow) \ + ON_ALL_ACTIONS(IdentifyWindow) \ + ON_ALL_ACTIONS(IdentifyWindows) \ + ON_ALL_ACTIONS(RenameWindow) \ + ON_ALL_ACTIONS(OpenWindowRenamer) \ + ON_ALL_ACTIONS(GlobalSummon) \ + ON_ALL_ACTIONS(QuakeMode) \ + ON_ALL_ACTIONS(FocusPane) \ + ON_ALL_ACTIONS(OpenSystemMenu) \ + ON_ALL_ACTIONS(ExportBuffer) \ + ON_ALL_ACTIONS(ClearBuffer) \ + ON_ALL_ACTIONS(MultipleActions) \ + ON_ALL_ACTIONS(Quit) \ + ON_ALL_ACTIONS(AdjustOpacity) \ + ON_ALL_ACTIONS(RestoreLastClosed) \ + ON_ALL_ACTIONS(SelectAll) \ + ON_ALL_ACTIONS(MarkMode) \ + ON_ALL_ACTIONS(ToggleBlockSelection) \ + ON_ALL_ACTIONS(SwitchSelectionEndpoint) \ + ON_ALL_ACTIONS(ColorSelection) #define ALL_SHORTCUT_ACTIONS_WITH_ARGS \ ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \ @@ -136,4 +137,5 @@ ON_ALL_ACTIONS_WITH_ARGS(ExportBuffer) \ ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \ ON_ALL_ACTIONS_WITH_ARGS(MultipleActions) \ - ON_ALL_ACTIONS_WITH_ARGS(AdjustOpacity) + ON_ALL_ACTIONS_WITH_ARGS(AdjustOpacity) \ + ON_ALL_ACTIONS_WITH_ARGS(ColorSelection) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 1d6f71c6344..8cdec793637 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -22,6 +22,7 @@ #include #include "userDefaults.h" +#include "enableColorSelection.h" #include "ApplicationState.h" #include "DefaultTerminal.h" @@ -313,6 +314,16 @@ void SettingsLoader::FinalizeLayering() { // Layer default globals -> user globals userSettings.globals->AddLeastImportantParent(inboxSettings.globals); + + // Actions are currently global, so if we want to conditionally light up a bunch of + // actions, this is the time to do it. + if (userSettings.globals->EnableColorSelection()) + { + const auto json = _parseJson(EnableColorSelectionSettingsJson); + const auto globals = GlobalAppSettings::FromJson(json.root); + userSettings.globals->AddLeastImportantParent(globals); + } + userSettings.globals->_FinalizeInheritance(); // Layer default profile defaults -> user profile defaults userSettings.baseLayerProfile->AddLeastImportantParent(inboxSettings.baseLayerProfile); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 849cdd40824..a94ba185107 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -94,6 +94,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, AlwaysShowNotificationIcon); INHERITABLE_SETTING(IVector, DisabledProfileSources); INHERITABLE_SETTING(Boolean, ShowAdminShield); + INHERITABLE_SETTING(Boolean, EnableColorSelection); Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index d95dbdfe43e..82a478f7801 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -60,7 +60,8 @@ Author(s): X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \ X(winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, "disabledProfileSources", nullptr) \ X(bool, ShowAdminShield, "showAdminShield", true) \ - X(bool, TrimPaste, "trimPaste", true) + X(bool, TrimPaste, "trimPaste", true) \ + X(bool, EnableColorSelection, "experimental.enableColorSelection", false) #define MTSM_PROFILE_SETTINGS(X) \ X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 5d1bab0f54e..1f9697688f4 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -301,6 +301,10 @@ + + + + diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 49135e63364..05cee101206 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -567,4 +567,92 @@ Switch selection endpoint + + , all matches + This is used in the description of an action which changes the color of selected text, to indicate that not only the selected text will be colored, but also all matching text. + + + Color selection, foreground: {0}{1} + This is the description of an action which changes the color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). + + + Color selection, background: {0}{1} + This is the description of an action which changes the background color of selected text. {0}: the color. {1}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). + + + Color selection, foreground: {0}, background: {1}{2} + This is the description of an action which changes the color of selected text and the background. {0}: the foreground color. {1}: the background color. {2}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). + + + Color selection, (default foreground/background){0} + This is the description of an action which changes the color of selected text and the background to the default colors. {0}: empty string OR a clause indicating that all matching text will be colored (ColorSelection_allMatches). + + + [default] + This is used in the description of an action which changes the color of selected text, as a placeholder for a color, to indicate that the default (foreground or background) color will be used. + + + black + A color used in the "ColorSelection" action. + + + red + A color used in the "ColorSelection" action. + + + green + A color used in the "ColorSelection" action. + + + yellow + A color used in the "ColorSelection" action. + + + blue + A color used in the "ColorSelection" action. + + + purple + A color used in the "ColorSelection" action. + + + cyan + A color used in the "ColorSelection" action. + + + white + A color used in the "ColorSelection" action. + + + bright black + A color used in the "ColorSelection" action. + + + bright red + A color used in the "ColorSelection" action. + + + bright green + A color used in the "ColorSelection" action. + + + bright yellow + A color used in the "ColorSelection" action. + + + bright blue + A color used in the "ColorSelection" action. + + + bright purple + A color used in the "ColorSelection" action. + + + bright cyan + A color used in the "ColorSelection" action. + + + bright white + A color used in the "ColorSelection" action. + diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index c3f3f1e1f5e..fe2a5ff2eec 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -679,3 +679,70 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection) pair_type{ "last", ValueType::Last }, }; }; + +template<> +struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winrt::Microsoft::Terminal::Control::SelectionColor> +{ + ::winrt::Microsoft::Terminal::Control::SelectionColor FromJson(const Json::Value& json) + { + winrt::Microsoft::Terminal::Control::SelectionColor selection{}; + auto str = Detail::GetStringView(json); + + if ((str.size() == 3) && (str.at(0) == 'i')) + { + auto indexStr = std::string(&str.at(1)); + // This will throw for something like "j0", but return 0 for something like + // "0j". + int idx = std::stoi(indexStr, 0, 16); + + til::color rgba = til::color(gsl::narrow_cast(idx), 0, 0); + // We need to manually convert to COLORREF up front, so that we can sneak in a + // special value into the "alpha" channel. + COLORREF cr = rgba; + cr |= 0x01000000; + selection.TextColor(cr); + } + else + { + til::color rgb = ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json)); + uint32_t val = (rgb.r << 24) | (rgb.g << 16) | (rgb.b << 8); + selection.TextColor(val); + } + return selection; + } + + bool CanConvert(const Json::Value& json) + { + if (!json.isString()) + { + return false; + } + + const auto string{ Detail::GetStringView(json) }; + + // Looks like "#NNN" or "#NNNNNN" (RGB) + // or "iNN" (index) + return ((string.length() == 7 || string.length() == 4) && string.front() == '#') || + ((string.length() == 3) && (string.front() == 'i')); + } + + Json::Value ToJson(const ::winrt::Microsoft::Terminal::Control::SelectionColor& val) + { + uint32_t raw = val.TextColor(); + + if ((raw & 0x01000000) == 0x01000000) + { + // It's an indexed color + return fmt::format("i{:02x}", (raw & 0x000000ff)); + } + else + { + return fmt::format("{:06x}", raw); + } + } + + std::string TypeDescription() const + { + return "either a hex \"#RRGGBB\" value, or a color index (\"iNN\")"; + } +}; diff --git a/src/cascadia/TerminalSettingsModel/enableColorSelection.json b/src/cascadia/TerminalSettingsModel/enableColorSelection.json new file mode 100644 index 00000000000..ad1efcd04b2 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/enableColorSelection.json @@ -0,0 +1,370 @@ +{ + "actions": + [ + // Foreground + { + "command": + { + "action": "experimental.colorSelection" + // default fg and bg (i07 and i00) + }, + "keys": "alt+1" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i08" + }, + "keys": "alt+2" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0c" + }, + "keys": "alt+3" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0a" + }, + "keys": "alt+4" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0e" + }, + "keys": "alt+5" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i09" + }, + "keys": "alt+6" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0d" + }, + "keys": "alt+7" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0b" + }, + "keys": "alt+8" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i0f" + }, + "keys": "alt+9" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i03" + }, + "keys": "alt+0" + }, + // background + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i07" + }, + "keys": "ctrl+1" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i08" + }, + "keys": "ctrl+2" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0c" + }, + "keys": "ctrl+3" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0a" + }, + "keys": "ctrl+4" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0e" + }, + "keys": "ctrl+5" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i09" + }, + "keys": "ctrl+6" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0d" + }, + "keys": "ctrl+7" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0b" + }, + "keys": "ctrl+8" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i0f" + }, + "keys": "ctrl+9" + }, + { + "command": + { + "action": "experimental.colorSelection", + "foreground": "i00", + "background": "i03" + }, + "keys": "ctrl+0" + }, + // with matching + // Foreground, all matches + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1 + // default fg and bg (i07 and i00) + }, + "keys": "alt+shift+1" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i08" + }, + "keys": "alt+shift+2" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0c" + }, + "keys": "alt+shift+3" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0a" + }, + "keys": "alt+shift+4" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0e" + }, + "keys": "alt+shift+5" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i09" + }, + "keys": "alt+shift+6" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0d" + }, + "keys": "alt+shift+7" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0b" + }, + "keys": "alt+shift+8" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i0f" + }, + "keys": "alt+shift+9" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i03" + }, + "keys": "alt+shift+0" + }, + // background, all matches + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i07" + }, + "keys": "ctrl+shift+1" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i08" + }, + "keys": "ctrl+shift+2" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0c" + }, + "keys": "ctrl+shift+3" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0a" + }, + "keys": "ctrl+shift+4" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0e" + }, + "keys": "ctrl+shift+5" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i09" + }, + "keys": "ctrl+shift+6" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0d" + }, + "keys": "ctrl+shift+7" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0b" + }, + "keys": "ctrl+shift+8" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i0f" + }, + "keys": "ctrl+shift+9" + }, + { + "command": + { + "action": "experimental.colorSelection", + "matchMode": 1, + "foreground": "i00", + "background": "i03" + }, + "keys": "ctrl+shift+0" + } + ] +} From e4271f1d550280b22a1166b97201a5d8fdc4bac5 Mon Sep 17 00:00:00 2001 From: "Dan Thompson (SBS)" Date: Tue, 2 Aug 2022 12:07:37 -0700 Subject: [PATCH 02/13] first (failed) attempt: put it in ControlCore.idl (Control::MatchMode) --- src/cascadia/TerminalControl/ControlCore.cpp | 4 +- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/ControlCore.idl | 10 ++++- src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/cascadia/TerminalControl/TermControl.h | 2 +- src/cascadia/TerminalControl/TermControl.idl | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 6 +-- src/cascadia/TerminalCore/Terminal.hpp | 7 +++- .../TerminalSettingsModel/ActionArgs.cpp | 9 ++--- .../TerminalSettingsModel/ActionArgs.h | 2 +- .../TerminalSettingsModel/ActionArgs.idl | 2 +- .../TerminalSettingsModel/EnumMappings.cpp | 1 + .../TerminalSettingsModel/EnumMappings.h | 1 + .../TerminalSettingsModel/EnumMappings.idl | 1 + .../TerminalSettingsSerializationHelpers.h | 8 ++++ .../enableColorSelection.json | 40 +++++++++---------- 16 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 74ee5d7614b..8f3f523e5de 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -2138,7 +2138,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode) + void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode) { if (HasSelection()) { @@ -2164,7 +2164,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->ColorSelection(attr, matchMode); _terminal->ClearSelection(); - if (matchMode > 0) + if (matchMode != MatchMode::None) { // ClearSelection will invalidate the selection area... but if we are // coloring other matches, then we need to make sure those get redrawn, diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 2ed80cf7653..512c2e8e508 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -130,7 +130,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Microsoft::Console::Types::IUiaData* GetUiaData() const; - void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode); + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode); void Close(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index a3b4bca2fe4..6565c27a54b 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -58,7 +58,13 @@ namespace Microsoft.Terminal.Control SelectionColor(); // Will be converted to a TextColor (from buffer/out/TextColor.h) UInt32 TextColor; - } + }; + + enum MatchMode + { + None, + All + }; [default_interface] runtimeclass ControlCore : ICoreState { @@ -139,7 +145,7 @@ namespace Microsoft.Terminal.Control void AdjustOpacity(Double Opacity, Boolean relative); void WindowVisibilityChanged(Boolean showOrHide); - void ColorSelection(SelectionColor fg, SelectionColor bg, UInt32 matchMode); + void ColorSelection(SelectionColor fg, SelectionColor bg, MatchMode matchMode); event FontSizeChangedEventArgs FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 117f1aef4f0..92421916e65 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -3063,7 +3063,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ScrollMarks(); } - void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode) + void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode) { _core.ColorSelection(fg, bg, matchMode); } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 89446fae7da..d2c30d6b9fe 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -48,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void WindowVisibilityChanged(const bool showOrHide); - void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, uint32_t matchMode); + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode); #pragma region ICoreState const uint64_t TaskbarState() const noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 0b989b150b1..698a9bae34f 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -93,6 +93,6 @@ namespace Microsoft.Terminal.Control Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; - void ColorSelection(SelectionColor fg, SelectionColor bg, UInt32 matchMode); + void ColorSelection(SelectionColor fg, SelectionColor bg, MatchMode matchMode); } } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index c8a17550b09..9ccaf8beca0 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1640,18 +1640,18 @@ til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal:: } } -void Terminal::ColorSelection(const TextAttribute& attr, uint32_t matchMode) +void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Control::MatchMode matchMode) { for (const auto [start, end] : _GetSelectionSpans()) { try { - if (matchMode == 0) + if (matchMode == winrt::Microsoft::Terminal::Control::MatchMode::None) { const auto length = _activeBuffer().SpanLength(start, end); _activeBuffer().Write(OutputCellIterator(attr, length), start); } - else if (matchMode == 1) + else if (matchMode == winrt::Microsoft::Terminal::Control::MatchMode::All) { const auto text = _activeBuffer().GetPlainText(/*trimTrailingWhitespace*/ IsBlockSelection(), start, end); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index e10e244fa16..3110da61027 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -42,6 +42,11 @@ namespace Microsoft::Terminal::Core class Terminal; } +namespace winrt::Microsoft::Terminal::Control +{ + enum class MatchMode; +} + // fwdecl unittest classes #ifdef UNIT_TESTING namespace TerminalCoreUnitTests @@ -232,7 +237,7 @@ class Microsoft::Terminal::Core::Terminal final : const size_t GetTaskbarState() const noexcept; const size_t GetTaskbarProgress() const noexcept; - void ColorSelection(const TextAttribute& attr, uint32_t matchMode); + void ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Control::MatchMode matchMode); #pragma region TextSelection // These methods are defined in TerminalSelection.cpp diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 947389f8ae5..fa63f27f4ea 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -952,15 +952,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ColorSelectionArgs::GenerateName() const { - auto matchMode = MatchMode() ? MatchMode() : 0; + auto matchMode = this->MatchMode(); auto matchModeStr = winrt::hstring{}; - if (matchMode) + if (matchMode == MatchMode::All) { - if (matchMode == 1) - { - matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" - } + matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" } bool hasForeground = (bool)Foreground(); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 5bba7206a02..476f1d74e9d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -240,7 +240,7 @@ private: #define COLOR_SELECTION_ARGS(X) \ X(winrt::Microsoft::Terminal::Control::SelectionColor, Foreground, "foreground", false, nullptr) \ X(winrt::Microsoft::Terminal::Control::SelectionColor, Background, "background", false, nullptr) \ - X(uint32_t, MatchMode, "matchMode", false, 0u) + X(winrt::Microsoft::Terminal::Control::MatchMode, MatchMode, "matchMode", false, winrt::Microsoft::Terminal::Control::MatchMode::None) //////////////////////////////////////////////////////////////////////////////// diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index fa0c01bbbf2..651c9d0c467 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -382,6 +382,6 @@ namespace Microsoft.Terminal.Settings.Model { Microsoft.Terminal.Control.SelectionColor Foreground; Microsoft.Terminal.Control.SelectionColor Background; - UInt32 MatchMode { get; }; + Microsoft.Terminal.Control.MatchMode MatchMode { get; }; }; } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 14b5ac9b0a9..a42589731c7 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::CopyFormat, CopyFormat); DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::MatchMode, MatchMode); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index fb89ee22257..7cf2a921cbb 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -44,6 +44,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap FontWeight(); static winrt::Windows::Foundation::Collections::IMap IntenseTextStyle(); static winrt::Windows::Foundation::Collections::IMap AdjustIndistinguishableColors(); + static winrt::Windows::Foundation::Collections::IMap MatchMode(); }; } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 6f704e2073a..800ddc4f6e2 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -16,6 +16,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap TabSwitcherMode { get; }; static Windows.Foundation.Collections.IMap CopyFormat { get; }; static Windows.Foundation.Collections.IMap WindowingMode { get; }; + static Windows.Foundation.Collections.IMap MatchMode { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index fe2a5ff2eec..027d49d96d5 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -76,6 +76,14 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollbarState) }; }; +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::MatchMode) +{ + static constexpr std::array mappings = { + pair_type{ "none", ValueType::None }, + pair_type{ "all", ValueType::All } + }; +}; + JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle) { static constexpr std::array mappings = { diff --git a/src/cascadia/TerminalSettingsModel/enableColorSelection.json b/src/cascadia/TerminalSettingsModel/enableColorSelection.json index ad1efcd04b2..3d26c2600da 100644 --- a/src/cascadia/TerminalSettingsModel/enableColorSelection.json +++ b/src/cascadia/TerminalSettingsModel/enableColorSelection.json @@ -179,7 +179,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1 + "matchMode": "All", // default fg and bg (i07 and i00) }, "keys": "alt+shift+1" @@ -188,7 +188,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i08" }, "keys": "alt+shift+2" @@ -197,7 +197,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0c" }, "keys": "alt+shift+3" @@ -206,7 +206,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0a" }, "keys": "alt+shift+4" @@ -215,7 +215,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0e" }, "keys": "alt+shift+5" @@ -224,7 +224,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i09" }, "keys": "alt+shift+6" @@ -233,7 +233,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0d" }, "keys": "alt+shift+7" @@ -242,7 +242,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0b" }, "keys": "alt+shift+8" @@ -251,7 +251,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i0f" }, "keys": "alt+shift+9" @@ -260,7 +260,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i03" }, "keys": "alt+shift+0" @@ -270,7 +270,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i07" }, @@ -280,7 +280,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i08" }, @@ -290,7 +290,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0c" }, @@ -300,7 +300,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0a" }, @@ -310,7 +310,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0e" }, @@ -320,7 +320,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i09" }, @@ -330,7 +330,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0d" }, @@ -340,7 +340,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0b" }, @@ -350,7 +350,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i0f" }, @@ -360,7 +360,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": 1, + "matchMode": "All", "foreground": "i00", "background": "i03" }, From 3320c1ad6dbc674e4daef7914c6e25bd47767952 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Aug 2022 16:25:24 +0200 Subject: [PATCH 03/13] Fix compilation with new MatchMode enum --- src/cascadia/TerminalControl/ControlCore.cpp | 4 ++-- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/ControlCore.idl | 8 +------- src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/cascadia/TerminalControl/TermControl.h | 2 +- src/cascadia/TerminalControl/TermControl.idl | 2 +- src/cascadia/TerminalCore/ICoreAppearance.idl | 6 ++++++ src/cascadia/TerminalCore/Terminal.cpp | 6 +++--- src/cascadia/TerminalCore/Terminal.hpp | 8 ++------ src/cascadia/TerminalSettingsModel/ActionArgs.cpp | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.h | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.idl | 2 +- src/cascadia/TerminalSettingsModel/EnumMappings.cpp | 2 +- src/cascadia/TerminalSettingsModel/EnumMappings.h | 2 +- src/cascadia/TerminalSettingsModel/EnumMappings.idl | 2 +- .../TerminalSettingsSerializationHelpers.h | 2 +- 16 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 8f3f523e5de..285dcfc2d41 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -2138,7 +2138,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode) + void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode) { if (HasSelection()) { @@ -2164,7 +2164,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->ColorSelection(attr, matchMode); _terminal->ClearSelection(); - if (matchMode != MatchMode::None) + if (matchMode != Core::MatchMode::None) { // ClearSelection will invalidate the selection area... but if we are // coloring other matches, then we need to make sure those get redrawn, diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 512c2e8e508..92063fe9a79 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -130,7 +130,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Microsoft::Console::Types::IUiaData* GetUiaData() const; - void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode); + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode); void Close(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 6565c27a54b..b81a7671bc8 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -60,12 +60,6 @@ namespace Microsoft.Terminal.Control UInt32 TextColor; }; - enum MatchMode - { - None, - All - }; - [default_interface] runtimeclass ControlCore : ICoreState { ControlCore(IControlSettings settings, @@ -145,7 +139,7 @@ namespace Microsoft.Terminal.Control void AdjustOpacity(Double Opacity, Boolean relative); void WindowVisibilityChanged(Boolean showOrHide); - void ColorSelection(SelectionColor fg, SelectionColor bg, MatchMode matchMode); + void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); event FontSizeChangedEventArgs FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 92421916e65..dc36983ed74 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -3063,7 +3063,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ScrollMarks(); } - void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode) + void TermControl::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode) { _core.ColorSelection(fg, bg, matchMode); } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index d2c30d6b9fe..220c5c2b57e 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -48,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void WindowVisibilityChanged(const bool showOrHide); - void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Control::MatchMode matchMode); + void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode); #pragma region ICoreState const uint64_t TaskbarState() const noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 698a9bae34f..1c85032ec3d 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -93,6 +93,6 @@ namespace Microsoft.Terminal.Control Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; - void ColorSelection(SelectionColor fg, SelectionColor bg, MatchMode matchMode); + void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); } } diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index bf8abaf85d6..6d314969e9c 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -3,6 +3,12 @@ namespace Microsoft.Terminal.Core { + enum MatchMode + { + None, + All + }; + enum CursorStyle { Vintage, diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9ccaf8beca0..530b8af9cd8 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1640,18 +1640,18 @@ til::color Terminal::GetColorForMark(const Microsoft::Console::VirtualTerminal:: } } -void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Control::MatchMode matchMode) +void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode) { for (const auto [start, end] : _GetSelectionSpans()) { try { - if (matchMode == winrt::Microsoft::Terminal::Control::MatchMode::None) + if (matchMode == winrt::Microsoft::Terminal::Core::MatchMode::None) { const auto length = _activeBuffer().SpanLength(start, end); _activeBuffer().Write(OutputCellIterator(attr, length), start); } - else if (matchMode == winrt::Microsoft::Terminal::Control::MatchMode::All) + else if (matchMode == winrt::Microsoft::Terminal::Core::MatchMode::All) { const auto text = _activeBuffer().GetPlainText(/*trimTrailingWhitespace*/ IsBlockSelection(), start, end); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 3110da61027..db9098a55b0 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::Core struct ICoreSettings; struct ICoreAppearance; struct Scheme; + enum class MatchMode; } namespace Microsoft::Console::VirtualTerminal @@ -42,11 +43,6 @@ namespace Microsoft::Terminal::Core class Terminal; } -namespace winrt::Microsoft::Terminal::Control -{ - enum class MatchMode; -} - // fwdecl unittest classes #ifdef UNIT_TESTING namespace TerminalCoreUnitTests @@ -237,7 +233,7 @@ class Microsoft::Terminal::Core::Terminal final : const size_t GetTaskbarState() const noexcept; const size_t GetTaskbarProgress() const noexcept; - void ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Control::MatchMode matchMode); + void ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode); #pragma region TextSelection // These methods are defined in TerminalSelection.cpp diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index fa63f27f4ea..fff97c7a61e 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -955,7 +955,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto matchMode = this->MatchMode(); auto matchModeStr = winrt::hstring{}; - if (matchMode == MatchMode::All) + if (matchMode == Core::MatchMode::All) { matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 476f1d74e9d..d857fcd1dc2 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -240,7 +240,7 @@ private: #define COLOR_SELECTION_ARGS(X) \ X(winrt::Microsoft::Terminal::Control::SelectionColor, Foreground, "foreground", false, nullptr) \ X(winrt::Microsoft::Terminal::Control::SelectionColor, Background, "background", false, nullptr) \ - X(winrt::Microsoft::Terminal::Control::MatchMode, MatchMode, "matchMode", false, winrt::Microsoft::Terminal::Control::MatchMode::None) + X(winrt::Microsoft::Terminal::Core::MatchMode, MatchMode, "matchMode", false, winrt::Microsoft::Terminal::Core::MatchMode::None) //////////////////////////////////////////////////////////////////////////////// diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 651c9d0c467..dba7587a69d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -382,6 +382,6 @@ namespace Microsoft.Terminal.Settings.Model { Microsoft.Terminal.Control.SelectionColor Foreground; Microsoft.Terminal.Control.SelectionColor Background; - Microsoft.Terminal.Control.MatchMode MatchMode { get; }; + Microsoft.Terminal.Core.MatchMode MatchMode { get; }; }; } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index a42589731c7..8b3d936a4a3 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -38,7 +38,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::CopyFormat, CopyFormat); DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode); - DEFINE_ENUM_MAP(Microsoft::Terminal::Control::MatchMode, MatchMode); + DEFINE_ENUM_MAP(Microsoft::Terminal::Core::MatchMode, MatchMode); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index 7cf2a921cbb..dfdd6c46502 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -34,6 +34,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap TabSwitcherMode(); static winrt::Windows::Foundation::Collections::IMap CopyFormat(); static winrt::Windows::Foundation::Collections::IMap WindowingMode(); + static winrt::Windows::Foundation::Collections::IMap MatchMode(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); @@ -44,7 +45,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap FontWeight(); static winrt::Windows::Foundation::Collections::IMap IntenseTextStyle(); static winrt::Windows::Foundation::Collections::IMap AdjustIndistinguishableColors(); - static winrt::Windows::Foundation::Collections::IMap MatchMode(); }; } diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 800ddc4f6e2..a74dc96b483 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -16,7 +16,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap TabSwitcherMode { get; }; static Windows.Foundation.Collections.IMap CopyFormat { get; }; static Windows.Foundation.Collections.IMap WindowingMode { get; }; - static Windows.Foundation.Collections.IMap MatchMode { get; }; + static Windows.Foundation.Collections.IMap MatchMode { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 027d49d96d5..c9d8843bf38 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -76,7 +76,7 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollbarState) }; }; -JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::MatchMode) +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Core::MatchMode) { static constexpr std::array mappings = { pair_type{ "none", ValueType::None }, From c7c86682a6ce4d1d91b40e5d664df1633a18cf8a Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Aug 2022 21:18:05 +0200 Subject: [PATCH 04/13] Fix invalid exec command in vcxproj --- .../Microsoft.Terminal.Settings.ModelLib.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 351ee4b3942..04f0f38d7b8 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -297,7 +297,7 @@ - + From 84fb3a783a4d0ba1b2ff5591cc8767e7cfc62d87 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Aug 2022 21:18:21 +0200 Subject: [PATCH 05/13] Fix color selection settings error --- .../enableColorSelection.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/enableColorSelection.json b/src/cascadia/TerminalSettingsModel/enableColorSelection.json index 3d26c2600da..0df6d750786 100644 --- a/src/cascadia/TerminalSettingsModel/enableColorSelection.json +++ b/src/cascadia/TerminalSettingsModel/enableColorSelection.json @@ -179,7 +179,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", // default fg and bg (i07 and i00) }, "keys": "alt+shift+1" @@ -188,7 +188,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i08" }, "keys": "alt+shift+2" @@ -197,7 +197,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0c" }, "keys": "alt+shift+3" @@ -206,7 +206,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0a" }, "keys": "alt+shift+4" @@ -215,7 +215,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0e" }, "keys": "alt+shift+5" @@ -224,7 +224,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i09" }, "keys": "alt+shift+6" @@ -233,7 +233,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0d" }, "keys": "alt+shift+7" @@ -242,7 +242,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0b" }, "keys": "alt+shift+8" @@ -251,7 +251,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i0f" }, "keys": "alt+shift+9" @@ -260,7 +260,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i03" }, "keys": "alt+shift+0" @@ -270,7 +270,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i07" }, @@ -280,7 +280,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i08" }, @@ -290,7 +290,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0c" }, @@ -300,7 +300,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0a" }, @@ -310,7 +310,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0e" }, @@ -320,7 +320,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i09" }, @@ -330,7 +330,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0d" }, @@ -340,7 +340,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0b" }, @@ -350,7 +350,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i0f" }, @@ -360,7 +360,7 @@ "command": { "action": "experimental.colorSelection", - "matchMode": "All", + "matchMode": "all", "foreground": "i00", "background": "i03" }, From d766d6c39775cd7fd0ab3687f9cf216fc10bed8c Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Aug 2022 21:19:34 +0200 Subject: [PATCH 06/13] Allow til::from_wchars to work with chars --- .../KeyChordSerialization.cpp | 2 +- src/inc/til/string.h | 51 ++++++++++++------- src/til/ut_til/string.cpp | 6 +-- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp index af50ccf97c7..50d2d135552 100644 --- a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp @@ -101,7 +101,7 @@ static int32_t parseNumericCode(const std::wstring_view& str, const std::wstring return 0; } - const auto value = til::from_wchars({ str.data() + prefix.size(), str.size() - prefix.size() - suffix.size() }); + const auto value = til::to_ulong({ str.data() + prefix.size(), str.size() - prefix.size() - suffix.size() }); if (value > 0 && value < 256) { return gsl::narrow_cast(value); diff --git a/src/inc/til/string.h b/src/inc/til/string.h index f7fb0ae757c..ac5fb2c1f4f 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -88,11 +88,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return ends_with<>(str, prefix); } - inline constexpr unsigned long from_wchars_error = ULONG_MAX; + inline constexpr unsigned long to_ulong_error = ULONG_MAX; // Just like std::wcstoul, but without annoying locales and null-terminating strings. // It has been fuzz-tested against clang's strtoul implementation. - _TIL_INLINEPREFIX unsigned long from_wchars(const std::wstring_view& str) noexcept + template + _TIL_INLINEPREFIX constexpr unsigned long to_ulong(const std::basic_string_view& str, unsigned long base = 0) noexcept { static constexpr unsigned long maximumValue = ULONG_MAX / 16; @@ -104,51 +105,55 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" #pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead auto ptr = str.data(); const auto end = ptr + str.length(); - unsigned long base = 10; unsigned long accumulator = 0; unsigned long value = ULONG_MAX; - if (str.length() > 1 && *ptr == L'0') + if (!base) { - base = 8; - ptr++; + base = 10; - if (str.length() > 2 && (*ptr == L'x' || *ptr == L'X')) + if (str.length() > 1 && *ptr == '0') { - base = 16; - ptr++; + base = 8; + ++ptr; + + if (str.length() > 2 && (*ptr == 'x' || *ptr == 'X')) + { + base = 16; + ++ptr; + } } } if (ptr == end) { - return from_wchars_error; + return to_ulong_error; } for (;; accumulator *= base) { value = ULONG_MAX; - if (*ptr >= L'0' && *ptr <= L'9') + if (*ptr >= '0' && *ptr <= '9') { - value = *ptr - L'0'; + value = *ptr - '0'; } - else if (*ptr >= L'A' && *ptr <= L'F') + else if (*ptr >= 'A' && *ptr <= 'F') { - value = *ptr - L'A' + 10; + value = *ptr - 'A' + 10; } - else if (*ptr >= L'a' && *ptr <= L'f') + else if (*ptr >= 'a' && *ptr <= 'f') { - value = *ptr - L'a' + 10; + value = *ptr - 'a' + 10; } else { - return from_wchars_error; + return to_ulong_error; } accumulator += value; if (accumulator >= maximumValue) { - return from_wchars_error; + return to_ulong_error; } if (++ptr == end) @@ -159,6 +164,16 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" #pragma warning(pop) } + constexpr unsigned long to_ulong(const std::string_view& str, unsigned long base = 0) noexcept + { + return to_ulong<>(str, base); + } + + constexpr unsigned long to_ulong(const std::wstring_view& str, unsigned long base = 0) noexcept + { + return to_ulong<>(str, base); + } + // Just like std::tolower, but without annoying locales. template constexpr T tolower_ascii(T c) diff --git a/src/til/ut_til/string.cpp b/src/til/ut_til/string.cpp index 76a12bc4a08..8e631718ae4 100644 --- a/src/til/ut_til/string.cpp +++ b/src/til/ut_til/string.cpp @@ -53,9 +53,9 @@ class StringTests VERIFY_IS_TRUE(til::ends_with("0abc", "abc")); } - // Normally this would be the spot where you'd find a TEST_METHOD(from_wchars). + // Normally this would be the spot where you'd find a TEST_METHOD(to_ulong). // I didn't quite trust my coding skills and thus opted to use fuzz-testing. - // The below function was used to test from_wchars for unsafety and conformance with clang's strtoul. + // The below function was used to test to_ulong for unsafety and conformance with clang's strtoul. // The test was run as: // clang++ -fsanitize=address,undefined,fuzzer -std=c++17 file.cpp // and was run for 20min across 16 jobs in parallel. @@ -93,7 +93,7 @@ class StringTests return 0; } - const auto actual = from_wchars({ wide_buffer, size }); + const auto actual = to_ulong({ wide_buffer, size }); if (expected != actual) { __builtin_trap(); From 715ee3eefbf4f96bc6007567e5121c57a8aafe6a Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Aug 2022 21:52:04 +0200 Subject: [PATCH 07/13] Various changes feat. lhecker In no particular order: * Fixed the json schema for the new colors and enums * Allow SelectionColor to work with full RGBA colors This is important to me personally as I plan to make Windows Terminal fully alpha channel aware * Use til::to_ulong for integer parsing This makes parsing simpler and faster since they support string_views * Simplify hashing, since we now use wyhash instead of FNV1a * Introduce til::point_span, since we actually need "spans" in a lot of other places as well * Simplify _FormatColorString using the USES_RESOURCE macro --- doc/cascadia/profiles.schema.json | 48 +++-- src/buffer/out/search.cpp | 14 +- src/buffer/out/search.h | 6 +- src/buffer/out/textBuffer.cpp | 61 ++---- src/buffer/out/textBuffer.hpp | 6 +- src/cascadia/TerminalControl/ControlCore.cpp | 20 +- src/cascadia/TerminalControl/ControlCore.h | 28 +-- src/cascadia/TerminalControl/ControlCore.idl | 6 +- src/cascadia/TerminalCore/Terminal.cpp | 11 +- src/cascadia/TerminalCore/Terminal.hpp | 2 +- .../TerminalCore/TerminalSelection.cpp | 4 +- .../TerminalSettingsModel/ActionArgs.cpp | 178 +++++++----------- .../TerminalSettingsModel/ActionArgs.h | 9 +- .../TerminalSettingsSerializationHelpers.h | 51 +++-- src/inc/til/point.h | 16 ++ 15 files changed, 195 insertions(+), 265 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 5c52a5f4def..ad8869a1dcf 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -14,6 +14,12 @@ "type": "string", "format": "color" }, + "ColorOrIndex": { + "default": "#", + "pattern": "^(?:#[A-Fa-f0-9]{3}(?:[A-Fa-f0-9]{3})?|i[A-Fa-f0-9]{2})$", + "type": "string", + "format": "color" + }, "Coordinates": { "pattern": "^(-?\\d+)?(,\\s?(-?\\d+)?)?$", "type": "string" @@ -58,20 +64,20 @@ "default": "", "description": "Sets the file location of the sound played when the application emits a BEL character. If the path is invalid no sound will be played. This property also accepts an array of sounds and the terminal will pick one at random.", "oneOf": [ - { - "type": [ - "string", - "null" - ] - }, - { - "type": "array", - "items": { - "type": "string" + { + "type": [ + "string", + "null" + ] + }, + { + "type": "array", + "items": { + "type": "string" + } } - } ] - }, + }, "AppearanceConfig": { "properties": { "colorScheme": { @@ -850,21 +856,25 @@ "const": "experimental.colorSelection" }, "matchMode": { - "default": 0, + "type": "string", + "default": "none", "description": "Specifies if only the selected text should be colored (0), or all instances of selected text (case-insensitive) (1).", - "maximum": 1, - "minimum": 0, - "type": "integer" + "enum": [ + "none", + "all" + ] }, "foreground": { + "$ref": "#/$defs/ColorOrIndex", "type": "string", "default": "", - "description": "The foreground color to use (either \"#RGB\" or \"#RRGGBB\" for an exact color, or \"iNN\" for an indexed color)." + "description": "The foreground color to use, as an RGB value (\"#rrggbb\"), or color index (\"iNN\"). If left unspecified it falls back to the default text foreground color." }, "background": { + "$ref": "#/$defs/ColorOrIndex", "type": "string", "default": "", - "description": "The background color to use (either \"#RGB\" or \"#RRGGBB\" for an exact color, or \"iNN\" for an indexed color)." + "description": "The background color to use, as an RGB value (\"#rrggbb\"), or color index (\"iNN\"). If left unspecified it falls back to the default text background color." } } } @@ -2014,7 +2024,7 @@ "afterLastTab", "afterCurrentTab" ], - "type": "string" + "type": "string" }, "autoHideWindow": { "default": false, diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index 67b7359ac2d..33c81eb1795 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -23,7 +23,7 @@ using namespace Microsoft::Console::Types; // - direction - The direction to search (upward or downward) // - sensitivity - Whether or not you care about case Search::Search(IUiaData& uiaData, - const std::wstring& str, + const std::wstring_view str, const Direction direction, const Sensitivity sensitivity) : _direction(direction), @@ -47,7 +47,7 @@ Search::Search(IUiaData& uiaData, // - sensitivity - Whether or not you care about case // - anchor - starting search location in screenInfo Search::Search(IUiaData& uiaData, - const std::wstring& str, + const std::wstring_view str, const Direction direction, const Sensitivity sensitivity, const til::point anchor) : @@ -111,9 +111,11 @@ void Search::Select() const // - attr - The attribute to apply to the result void Search::Color(const TextAttribute attr) const { - // Note that _coordSelStart may be equal to _coordSelEnd (but it's an inclusive - // selection: if they are equal, it means we are applying to a single character). - _uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr); + // Only select if we've found something. + if (_coordSelStart >= _coordSelEnd) + { + _uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr); + } } // Routine Description: @@ -327,7 +329,7 @@ void Search::_UpdateNextPosition() // - wstr - String that will be our search term // Return Value: // - Structured text data for comparison to screen buffer text data. -std::vector> Search::s_CreateNeedleFromString(const std::wstring& wstr) +std::vector> Search::s_CreateNeedleFromString(const std::wstring_view wstr) { const auto charData = Utf16Parser::Parse(wstr); std::vector> cells; diff --git a/src/buffer/out/search.h b/src/buffer/out/search.h index 7f30245ec5b..c5581f2bf8b 100644 --- a/src/buffer/out/search.h +++ b/src/buffer/out/search.h @@ -41,12 +41,12 @@ class Search final }; Search(Microsoft::Console::Types::IUiaData& uiaData, - const std::wstring& str, + const std::wstring_view str, const Direction dir, const Sensitivity sensitivity); Search(Microsoft::Console::Types::IUiaData& uiaData, - const std::wstring& str, + const std::wstring_view str, const Direction dir, const Sensitivity sensitivity, const til::point anchor); @@ -68,7 +68,7 @@ class Search final static til::point s_GetInitialAnchor(const Microsoft::Console::Types::IUiaData& uiaData, const Direction dir); - static std::vector> s_CreateNeedleFromString(const std::wstring& wstr); + static std::vector> s_CreateNeedleFromString(const std::wstring_view wstr); bool _reachedEnd = false; til::point _coordNext; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index b393a1adf8c..d7782e5b275 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1672,9 +1672,9 @@ const std::vector TextBuffer::GetTextRects(til::point start // the buffer rather than the screen. // Return Value: // - one or more sets of start-end coordinates, representing spans of text in the buffer -const std::vector> TextBuffer::GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const +std::vector TextBuffer::GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const { - std::vector> textSpans; + std::vector textSpans; if (blockSelection) { @@ -1687,8 +1687,7 @@ const std::vector> TextBuffer::GetTextSpans(t { til::point first = { rect.Left, rect.Top }; til::point second = { rect.Right, rect.Bottom }; - auto span = std::make_tuple(first, second); - textSpans.emplace_back(span); + textSpans.emplace_back(first, second); } } else @@ -1719,8 +1718,7 @@ const std::vector> TextBuffer::GetTextSpans(t lowerCoord.X = asRect.Right; lowerCoord.Y = asRect.Bottom; - auto span = std::make_tuple(higherCoord, lowerCoord); - textSpans.emplace_back(span); + textSpans.emplace_back(higherCoord, lowerCoord); } return textSpans; @@ -1840,9 +1838,8 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF, } } } -#pragma warning(suppress : 26444) - // TODO GH 2675: figure out why there's custom construction/destruction happening here - it++; + + ++it; } // We apply formatting to rows if the row was NOT wrapped or formatting of wrapped rows is allowed @@ -1900,25 +1897,10 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF, size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coordEnd) const { - assert((coordEnd.Y > coordStart.Y) || - ((coordEnd.Y == coordStart.Y) && (coordEnd.X >= coordStart.X))); - - // Note that this could also be computed using CompareInBounds, but that function - // seems disfavored lately. - // - // CompareInBounds version: - // - // const auto bufferSize = GetSize(); - // // Note that we negate because CompareInBounds is backwards from what we are trying to calculate. - // auto length = - bufferSize.CompareInBounds(coordStart, coordEnd); - // length += 1; // because we need "inclusive" behavior. - - const auto rowSize = gsl::narrow(GetRowByOffset(0).size()); - - size_t length = (static_cast(coordEnd.Y) - coordStart.Y) * rowSize; - length += (static_cast(coordEnd.X) - coordStart.X) + 1; // "+1" is because we need "inclusive" behavior - - return length; + const auto bufferSize = GetSize(); + // The coords are inclusive, so to get the (inclusive) length we add 1. + const auto length = bufferSize.CompareInBounds(coordEnd, coordStart) + 1; + return gsl::narrow(length); } // Routine Description: @@ -1929,41 +1911,22 @@ size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coor // - end - where to end getting text // Return Value: // - Just the text. -const std::wstring TextBuffer::GetPlainText(const bool trimTrailingWhitespace, - const til::point& start, - const til::point& end) const +std::wstring TextBuffer::GetPlainText(const til::point& start, const til::point& end) const { std::wstring text; - // TODO: should I put in protections for start coming before end? auto spanLength = SpanLength(start, end); text.reserve(spanLength); auto it = GetCellDataAt(start); - // copy char data into the string buffer, skipping trailing bytes - // TODO: is using spanLength like this the right way to do it? - while (it && ((spanLength) > 0)) + for (; it && spanLength > 0; ++it, --spanLength) { const auto& cell = *it; - spanLength--; - if (!cell.DbcsAttr().IsTrailing()) { const auto chars = cell.Chars(); text.append(chars); } -#pragma warning(suppress : 26444) - // TODO GH 2675: figure out why there's custom construction/destruction happening here - it++; - } - - if (trimTrailingWhitespace) - { - // remove the spaces at the end (aka trim the trailing whitespace) - while (!text.empty() && text.back() == UNICODE_SPACE) - { - text.pop_back(); - } } return text; diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index c0cc35681c1..17cfb21b6da 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -166,7 +166,7 @@ class TextBuffer final bool MoveToPreviousGlyph(til::point& pos, std::optional limitOptional = std::nullopt) const; const std::vector GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const; - const std::vector> GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const; + std::vector GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const; void AddHyperlinkToMap(std::wstring_view uri, uint16_t id); std::wstring GetHyperlinkUriFromId(uint16_t id) const; @@ -191,9 +191,7 @@ class TextBuffer final std::function(const TextAttribute&)> GetAttributeColors = nullptr, const bool formatWrappedRows = false) const; - const std::wstring GetPlainText(const bool trimTrailingWhitespace, - const til::point& start, - const til::point& end) const; + std::wstring GetPlainText(const til::point& start, const til::point& end) const; static std::string GenHTML(const TextAndColor& rows, const int fontHeightPoints, diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index fc427cd1710..7144453571f 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -16,8 +16,8 @@ #include "../../renderer/atlas/AtlasEngine.h" #include "../../renderer/dx/DxRenderer.hpp" -#include "SelectionColor.g.cpp" #include "ControlCore.g.cpp" +#include "SelectionColor.g.cpp" using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::VirtualTerminal; @@ -79,6 +79,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation return initialized; } + TextColor SelectionColor::AsTextColor() const noexcept + { + if (_IsIndex16) + { + return { _Color.r, false }; + } + else + { + return { static_cast(_Color) }; + } + } + ControlCore::ControlCore(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : @@ -2139,7 +2151,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode) + void ControlCore::ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode) { if (HasSelection()) { @@ -2151,12 +2163,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (pForeground) { - foregroundAsTextColor = pForeground->Color(); + foregroundAsTextColor = pForeground->AsTextColor(); } if (pBackground) { - backgroundAsTextColor = pBackground->Color(); + backgroundAsTextColor = pBackground->AsTextColor(); } TextAttribute attr; diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index d99761943ed..b0425008290 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -15,8 +15,8 @@ #pragma once -#include "SelectionColor.g.h" #include "ControlCore.g.h" +#include "SelectionColor.g.h" #include "ControlSettings.h" #include "../../audio/midi/MidiAudio.hpp" #include "../../renderer/base/Renderer.hpp" @@ -44,26 +44,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation { struct SelectionColor : SelectionColorT { - SelectionColor() = default; - WINRT_PROPERTY(uint32_t, TextColor); - - public: - ::TextColor Color() const - { - ::TextColor asTextColor; - - // High bits set indicate an indexed color. - if (_TextColor & 0xff000000) - { - asTextColor.SetIndex(_TextColor & 0xff, false); - } - else - { - asTextColor.SetColor(_TextColor); - } + TextColor AsTextColor() const noexcept; - return asTextColor; - }; + WINRT_PROPERTY(til::color, Color); + WINRT_PROPERTY(bool, IsIndex16); }; struct ControlCore : ControlCoreT @@ -130,7 +114,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Microsoft::Console::Types::IUiaData* GetUiaData() const; - void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode); + void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode); void Close(); @@ -365,6 +349,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation namespace winrt::Microsoft::Terminal::Control::factory_implementation { - BASIC_FACTORY(SelectionColor); BASIC_FACTORY(ControlCore); + BASIC_FACTORY(SelectionColor); } diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index b81a7671bc8..28238bcef53 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -56,8 +56,10 @@ namespace Microsoft.Terminal.Control [default_interface] runtimeclass SelectionColor { SelectionColor(); - // Will be converted to a TextColor (from buffer/out/TextColor.h) - UInt32 TextColor; + Microsoft.Terminal.Core.Color Color; + // If true, color.R is a value between 0 and 15, indicating an indexed color. + // This mirrors how TextColor works internally, which is the primary target of this interface. + Boolean IsIndex16; }; [default_interface] runtimeclass ControlCore : ICoreState diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 530b8af9cd8..1339010ee65 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1648,12 +1648,17 @@ void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Termi { if (matchMode == winrt::Microsoft::Terminal::Core::MatchMode::None) { - const auto length = _activeBuffer().SpanLength(start, end); - _activeBuffer().Write(OutputCellIterator(attr, length), start); + ColorSelection(start, end, attr); } else if (matchMode == winrt::Microsoft::Terminal::Core::MatchMode::All) { - const auto text = _activeBuffer().GetPlainText(/*trimTrailingWhitespace*/ IsBlockSelection(), start, end); + const auto textBuffer = _activeBuffer().GetPlainText(start, end); + std::wstring_view text{ textBuffer }; + + if (IsBlockSelection()) + { + text = Utils::TrimPaste(text); + } if (!text.empty()) { diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index db9098a55b0..c57617de2da 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -434,7 +434,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; - std::vector> _GetSelectionSpans() const noexcept; + std::vector _GetSelectionSpans() const noexcept; std::pair _PivotSelection(const til::point targetPos, bool& targetStart) const; std::pair _ExpandSelectionAnchors(std::pair anchors) const; til::point _ConvertToBufferCell(const til::point viewportPos) const; diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 257ecfd3ef5..0ff61bddc1c 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -67,9 +67,9 @@ std::vector Terminal::_GetSelectionRects() const noexcept // - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection. // Return Value: // - A vector of one or more spans representing the selection. They are absolute coordinates relative to the buffer origin. -std::vector> Terminal::_GetSelectionSpans() const noexcept +std::vector Terminal::_GetSelectionSpans() const noexcept { - std::vector> result; + std::vector result; if (!IsSelectionActive()) { diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index fff97c7a61e..850411e1be5 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -842,129 +842,81 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } - winrt::hstring _FormatColorString(uint32_t color) + static winrt::hstring _FormatColorString(const Control::SelectionColor& selectionColor) { + if (!selectionColor) + { + return RS_(L"ColorSelection_defaultColor"); + } + + const auto color = selectionColor.Color(); + const auto isIndexed16 = selectionColor.IsIndex16(); winrt::hstring colorStr; - if ((color & 0x01000000) == 0x01000000) - { - // It's an indexed color. - uint8_t idx = color & 0x000000ff; + if (isIndexed16) + { + static const std::array indexedColorNames{ + USES_RESOURCE(L"ColorSelection_Black"), + USES_RESOURCE(L"ColorSelection_Red"), + USES_RESOURCE(L"ColorSelection_Green"), + USES_RESOURCE(L"ColorSelection_Yellow"), + USES_RESOURCE(L"ColorSelection_Blue"), + USES_RESOURCE(L"ColorSelection_Purple"), + USES_RESOURCE(L"ColorSelection_Cyan"), + USES_RESOURCE(L"ColorSelection_White"), + USES_RESOURCE(L"ColorSelection_BrightBlack"), + USES_RESOURCE(L"ColorSelection_BrightRed"), + USES_RESOURCE(L"ColorSelection_BrightGreen"), + USES_RESOURCE(L"ColorSelection_BrightYellow"), + USES_RESOURCE(L"ColorSelection_BrightBlue"), + USES_RESOURCE(L"ColorSelection_BrightPurple"), + USES_RESOURCE(L"ColorSelection_BrightCyan"), + USES_RESOURCE(L"ColorSelection_BrightWhite"), + }; + static_assert(indexedColorNames.size() == 16); - switch (idx) + if (color.R < indexedColorNames.size()) + { + colorStr = GetLibraryResourceString(til::at(indexedColorNames, color.R)); + } + else { - case 0: - colorStr = RS_(L"ColorSelection_Black"); // "black" - break; - - case 1: - // AKA "dark red" - colorStr = RS_(L"ColorSelection_Red"); // "red" - break; - - case 2: - // AKA "dark green" - colorStr = RS_(L"ColorSelection_Green"); // "green" - break; - - case 3: - // AKA "dark yellow" - colorStr = RS_(L"ColorSelection_Yellow"); // "yellow" - break; - - case 4: - // AKA "dark blue" - colorStr = RS_(L"ColorSelection_Blue"); // "blue" - break; - - case 5: - // AKA "dark magenta" - colorStr = RS_(L"ColorSelection_Purple"); // "purple" - break; - - case 6: - // AK "dark cyan" - colorStr = RS_(L"ColorSelection_Cyan"); // "cyan" - break; - - case 7: - // AKA "gray" / "dark white" - colorStr = RS_(L"ColorSelection_White"); // "white" - break; - - case 8: - // AKA "dark gray" - colorStr = RS_(L"ColorSelection_BrightBlack"); // "bright black" - break; - - case 9: - // AKA "red" - colorStr = RS_(L"ColorSelection_BrightRed"); // "bright red" - break; - - case 10: - // AKA "green" - colorStr = RS_(L"ColorSelection_BrightGreen"); // "bright green" - break; - - case 11: - // AKA "yellow" - colorStr = RS_(L"ColorSelection_BrightYellow"); // "bright yellow" - break; - - case 12: - // AKA "blue" - colorStr = RS_(L"ColorSelection_BrightBlue"); // "bright blue" - break; - - case 13: - // AKA "magenta" - colorStr = RS_(L"ColorSelection_BrightPurple"); // "bright purple" - break; - - case 14: - // AKA "cyan" - colorStr = RS_(L"ColorSelection_BrightCyan"); // "bright cyan" - break; - - case 15: - // AKA "white" - colorStr = RS_(L"ColorSelection_BrightWhite"); // "bright white" - break; - - default: wchar_t tempBuf[9] = { 0 }; - swprintf_s(tempBuf, L"i%02i", idx); + swprintf_s(tempBuf, L"i%02i", color.R); colorStr = tempBuf; } } else { - wchar_t tempBuf[9] = { 0 }; - auto err = _itow_s(color, tempBuf, 16); - assert(err == 0); - UNREFERENCED_PARAMETER(err); - colorStr = tempBuf; + colorStr = til::color{ color }.ToHexString(true); } return colorStr; } - winrt::hstring ColorSelectionArgs::GenerateName() const + static bool _isBoringColor(const Control::SelectionColor& selectionColor) { - auto matchMode = this->MatchMode(); + if (!selectionColor) + { + return true; + } + + const til::color color{ selectionColor.Color() }; + return color.with_alpha(0) == til::color{}; + } + winrt::hstring ColorSelectionArgs::GenerateName() const + { auto matchModeStr = winrt::hstring{}; - if (matchMode == Core::MatchMode::All) + if (MatchMode() == Core::MatchMode::All) { matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" } - bool hasForeground = (bool)Foreground(); - bool hasBackground = (bool)Background(); - - winrt::hstring fgStr = hasForeground ? _FormatColorString(Foreground().TextColor()) : RS_(L"ColorSelection_defaultColor"); // "[default]" - winrt::hstring bgStr = hasBackground ? _FormatColorString(Background().TextColor()) : RS_(L"ColorSelection_defaultColor"); // "[default]" + const auto foreground = Foreground(); + const auto background = Background(); + const auto fgStr = _FormatColorString(foreground); + const auto bgStr = _FormatColorString(background); // To try to keep things simple for the user, we'll try to show only the // "interesting" color (i.e. leave off the bg or fg if it is either unspecified or @@ -972,35 +924,35 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // // Note that we mask off the alpha channel, which is used to indicate if it's an // indexed color. - bool foregroundIsExplicitBlack = hasForeground && (Foreground().TextColor() & 0x00ffffff) == 0; - bool backgroundIsExplicitBlack = hasBackground && (Background().TextColor() & 0x00ffffff) == 0; + const auto foregroundIsBoring = _isBoringColor(foreground); + const auto backgroundIsBoring = _isBoringColor(background); - if (hasForeground && (!hasBackground || backgroundIsExplicitBlack)) + if (foreground && backgroundIsBoring) { - auto str = RS_(L"ColorSelection_fg_action"); // "Color selection, foreground: {0}{1}" + const auto str = RS_(L"ColorSelection_fg_action"); // "Color selection, foreground: {0}{1}" return winrt::hstring{ - fmt::format(str.c_str(), fgStr, matchModeStr) + fmt::format(std::wstring_view{ str }, fgStr, matchModeStr) }; } - else if (hasBackground && (!hasForeground || foregroundIsExplicitBlack)) + else if (background && foregroundIsBoring) { - auto str = RS_(L"ColorSelection_bg_action"); // "Color selection, background: {0}{1}" + const auto str = RS_(L"ColorSelection_bg_action"); // "Color selection, background: {0}{1}" return winrt::hstring{ - fmt::format(str.c_str(), bgStr, matchModeStr) + fmt::format(std::wstring_view{ str }, bgStr, matchModeStr) }; } - else if (hasForeground && hasBackground) + else if (foreground && background) { - auto str = RS_(L"ColorSelection_fg_bg_action"); // "Color selection, foreground: {0}, background: {1}{2}" + const auto str = RS_(L"ColorSelection_fg_bg_action"); // "Color selection, foreground: {0}, background: {1}{2}" return winrt::hstring{ - fmt::format(str.c_str(), fgStr, bgStr, matchModeStr) + fmt::format(std::wstring_view{ str }, fgStr, bgStr, matchModeStr) }; } else { - auto str = RS_(L"ColorSelection_default_action"); // "Color selection, (default foreground/background){0}" + const auto str = RS_(L"ColorSelection_default_action"); // "Color selection, (default foreground/background){0}" return winrt::hstring{ - fmt::format(str.c_str(), matchModeStr) + fmt::format(std::wstring_view{ str }, matchModeStr) }; } } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index d857fcd1dc2..7dc91ac32e6 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -400,13 +400,8 @@ struct til::hash_trait { if (value) { - h.write(value.TextColor()); - } - else - { - // N.B. it is important even for a non-value to contribute to the hash, else - // it is easier to have hash collisions. - h.write(-1); + h.write(value.Color()); + h.write(value.IsIndex16()); } } }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index c9d8843bf38..a26c0b7e73d 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -693,29 +693,24 @@ struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winr { ::winrt::Microsoft::Terminal::Control::SelectionColor FromJson(const Json::Value& json) { - winrt::Microsoft::Terminal::Control::SelectionColor selection{}; - auto str = Detail::GetStringView(json); + const auto string = Detail::GetStringView(json); + const auto isIndexed16 = string.size() == 3 && string.front() == 'i'; + til::color color; - if ((str.size() == 3) && (str.at(0) == 'i')) + if (isIndexed16) { - auto indexStr = std::string(&str.at(1)); - // This will throw for something like "j0", but return 0 for something like - // "0j". - int idx = std::stoi(indexStr, 0, 16); - - til::color rgba = til::color(gsl::narrow_cast(idx), 0, 0); - // We need to manually convert to COLORREF up front, so that we can sneak in a - // special value into the "alpha" channel. - COLORREF cr = rgba; - cr |= 0x01000000; - selection.TextColor(cr); + const auto indexStr = string.substr(1); + const auto idx = til::to_ulong(indexStr, 16); + color.r = gsl::narrow_cast(std::min(idx, 15ul)); } else { - til::color rgb = ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json)); - uint32_t val = (rgb.r << 24) | (rgb.g << 16) | (rgb.b << 8); - selection.TextColor(val); + color = ::Microsoft::Console::Utils::ColorFromHexString(string); } + + winrt::Microsoft::Terminal::Control::SelectionColor selection; + selection.Color(color); + selection.IsIndex16(isIndexed16); return selection; } @@ -726,31 +721,27 @@ struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winr return false; } - const auto string{ Detail::GetStringView(json) }; - - // Looks like "#NNN" or "#NNNNNN" (RGB) - // or "iNN" (index) - return ((string.length() == 7 || string.length() == 4) && string.front() == '#') || - ((string.length() == 3) && (string.front() == 'i')); + const auto string = Detail::GetStringView(json); + const auto isColorSpec = (string.length() == 9 || string.length() == 7 || string.length() == 4) && string.front() == '#'; + const auto isIndexedColor = string.size() == 3 && string.front() == 'i'; + return isColorSpec || isIndexedColor; } Json::Value ToJson(const ::winrt::Microsoft::Terminal::Control::SelectionColor& val) { - uint32_t raw = val.TextColor(); - - if ((raw & 0x01000000) == 0x01000000) + const auto color = val.Color(); + if (val.IsIndex16()) { - // It's an indexed color - return fmt::format("i{:02x}", (raw & 0x000000ff)); + return fmt::format("i{:02x}", color.R); } else { - return fmt::format("{:06x}", raw); + return ::Microsoft::Console::Utils::ColorToHexString(color); } } std::string TypeDescription() const { - return "either a hex \"#RRGGBB\" value, or a color index (\"iNN\")"; + return "SelectionColor (#rrggbb, #rgb, #rrggbbaa, iNN)"; } }; diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 4177e448a0a..29d2a9e2f5a 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -270,6 +270,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } RETURN_WIN32(ERROR_UNHANDLED_EXCEPTION); } + + // point_span can be pictured as a "selection" range inside our text buffer. So given + // a text buffer of 10x4, a start of 4,1 and end of 7,3 the span might look like this: + // +----------+ + // | | + // | xxxxxx| + // |xxxxxxxxxx| + // |xxxxxxxx | + // +----------+ + // At the time of writing there's a push to make selections have an exclusive end coordinate, + // so the interpretation of end might change soon (making this comment potentially outdated). + struct point_span + { + til::point start; + til::point end; + }; } #ifdef __WEX_COMMON_H__ From f2a0d7b81d812dbaa4b937e5be82b41962d05504 Mon Sep 17 00:00:00 2001 From: "Dan Thompson (SBS)" Date: Thu, 18 Aug 2022 00:34:17 -0700 Subject: [PATCH 08/13] just a little oops (transposed start and end coords in comparison) --- src/buffer/out/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp index 33c81eb1795..6bbcdc1ae68 100644 --- a/src/buffer/out/search.cpp +++ b/src/buffer/out/search.cpp @@ -112,7 +112,7 @@ void Search::Select() const void Search::Color(const TextAttribute attr) const { // Only select if we've found something. - if (_coordSelStart >= _coordSelEnd) + if (_coordSelEnd >= _coordSelStart) { _uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr); } From 1915ca94b59d75ac5bc28e033755fd90b0651c93 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 19 Aug 2022 11:39:25 -0700 Subject: [PATCH 09/13] [ActionMap] remove FAIL_FAST on >1 parent --- src/cascadia/TerminalSettingsModel/ActionMap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 5809369a660..a3a5124a3d0 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -514,7 +514,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (maskingActionPair == _MaskingActions.end()) { // Check if we need to add this to our list of masking commands. - FAIL_FAST_IF(_parents.size() > 1); for (const auto& parent : _parents) { // NOTE: This only checks the layer above us, but that's ok. From cf662898a0decb04baad6ef7a8a3c0088fb5aecd Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 19 Aug 2022 14:04:07 -0700 Subject: [PATCH 10/13] address carlos-zamora's comments --- doc/cascadia/profiles.schema.json | 2 +- src/buffer/out/textBuffer.cpp | 73 +++---------------- .../TerminalSettingsModel/ActionArgs.cpp | 2 +- .../Resources/en-US/Resources.resw | 2 +- 4 files changed, 15 insertions(+), 64 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index ad8869a1dcf..a6cb8aef369 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1782,7 +1782,7 @@ }, "experimental.enableColorSelection": { "default": false, - "description": "When set to true, adds 40 preset \"Color Selection\" actions (keybindings) to allow colorizing selected text via keystroke, similar to the legacy conhost EnableColorSelection feature (such as alt+6 to color the selection red).", + "description": "When set to true, adds preset \"Color Selection\" actions (keybindings) to allow colorizing selected text via keystroke, similar to the legacy conhost EnableColorSelection feature (such as alt+6 to color the selection red).", "type": "boolean" }, "disableAnimations": { diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index d7782e5b275..e3696d55b12 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1674,53 +1674,17 @@ const std::vector TextBuffer::GetTextRects(til::point start // - one or more sets of start-end coordinates, representing spans of text in the buffer std::vector TextBuffer::GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const { + // This is effectively the same operation as GetTextRects, + // but expressed in til::point coordinates. std::vector textSpans; - - if (blockSelection) + const auto rects = GetTextRects(start, end, blockSelection, bufferCoordinates); + textSpans.reserve(rects.size()); + for (const auto rect : rects) { - // If blockSelection, this is effectively the same operation as GetTextRects, but - // expressed in til::point coordinates. - auto rects = GetTextRects(start, end, /*blockSelection*/ true, bufferCoordinates); - textSpans.reserve(rects.size()); - - for (auto rect : rects) - { - til::point first = { rect.Left, rect.Top }; - til::point second = { rect.Right, rect.Bottom }; - textSpans.emplace_back(first, second); - } + const til::point first{ rect.Left, rect.Top }; + const til::point second{ rect.Right, rect.Bottom }; + textSpans.emplace_back(first, second); } - else - { - const auto bufferSize = GetSize(); - - // (0,0) is the top-left of the screen - // the physically "higher" coordinate is closer to the top-left - // the physically "lower" coordinate is closer to the bottom-right - auto [higherCoord, lowerCoord] = start <= end ? - std::make_tuple(start, end) : - std::make_tuple(end, start); - - textSpans.reserve(1); - - // If we were passed screen coordinates, convert the given range into - // equivalent buffer offsets, taking line rendition into account. - if (!bufferCoordinates) - { - higherCoord = ScreenToBufferLine(higherCoord, GetLineRendition(higherCoord.Y)); - lowerCoord = ScreenToBufferLine(lowerCoord, GetLineRendition(lowerCoord.Y)); - } - - til::inclusive_rect asRect = { higherCoord.X, higherCoord.Y, lowerCoord.X, lowerCoord.Y }; - _ExpandTextRow(asRect); - higherCoord.X = asRect.Left; - higherCoord.Y = asRect.Top; - lowerCoord.X = asRect.Right; - lowerCoord.Y = asRect.Bottom; - - textSpans.emplace_back(higherCoord, lowerCoord); - } - return textSpans; } @@ -1913,23 +1877,10 @@ size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coor // - Just the text. std::wstring TextBuffer::GetPlainText(const til::point& start, const til::point& end) const { - std::wstring text; - auto spanLength = SpanLength(start, end); - text.reserve(spanLength); - - auto it = GetCellDataAt(start); - - for (; it && spanLength > 0; ++it, --spanLength) - { - const auto& cell = *it; - if (!cell.DbcsAttr().IsTrailing()) - { - const auto chars = cell.Chars(); - text.append(chars); - } - } - - return text; + const auto& buffer = _activeBuffer(); + const auto rects = buffer.GetTextRects(start, end, false, true); + const auto textData = buffer.GetText(false, false, rects, nullptr, false); + return textData.text; } // Routine Description: diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 850411e1be5..a108b200e60 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -910,7 +910,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto matchModeStr = winrt::hstring{}; if (MatchMode() == Core::MatchMode::All) { - matchModeStr = RS_(L"ColorSelection_allMatches"); // ", all matches" + matchModeStr = fmt::format(std::wstring_view{ L", {}" }, RS_(L"ColorSelection_allMatches")); // ", all matches" } const auto foreground = Foreground(); diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 05cee101206..e585eb5e83c 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -568,7 +568,7 @@ Switch selection endpoint - , all matches + all matches This is used in the description of an action which changes the color of selected text, to indicate that not only the selected text will be colored, but also all matching text. From 26e948c371d04036eeb994240e365d441c5afc5c Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 22 Aug 2022 11:38:51 -0700 Subject: [PATCH 11/13] revert changes to textBuffer.cpp --- src/buffer/out/textBuffer.cpp | 72 +++++++++++++++---- .../TerminalSettingsModel/ActionArgs.cpp | 2 +- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index e3696d55b12..2c7e0359387 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1674,17 +1674,52 @@ const std::vector TextBuffer::GetTextRects(til::point start // - one or more sets of start-end coordinates, representing spans of text in the buffer std::vector TextBuffer::GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const { - // This is effectively the same operation as GetTextRects, - // but expressed in til::point coordinates. std::vector textSpans; - const auto rects = GetTextRects(start, end, blockSelection, bufferCoordinates); - textSpans.reserve(rects.size()); - for (const auto rect : rects) + if (blockSelection) { - const til::point first{ rect.Left, rect.Top }; - const til::point second{ rect.Right, rect.Bottom }; - textSpans.emplace_back(first, second); + // If blockSelection, this is effectively the same operation as GetTextRects, but + // expressed in til::point coordinates. + const auto rects = GetTextRects(start, end, /*blockSelection*/ true, bufferCoordinates); + textSpans.reserve(rects.size()); + + for (auto rect : rects) + { + const til::point first = { rect.Left, rect.Top }; + const til::point second = { rect.Right, rect.Bottom }; + textSpans.emplace_back(first, second); + } + } + else + { + const auto bufferSize = GetSize(); + + // (0,0) is the top-left of the screen + // the physically "higher" coordinate is closer to the top-left + // the physically "lower" coordinate is closer to the bottom-right + auto [higherCoord, lowerCoord] = start <= end ? + std::make_tuple(start, end) : + std::make_tuple(end, start); + + textSpans.reserve(1); + + // If we were passed screen coordinates, convert the given range into + // equivalent buffer offsets, taking line rendition into account. + if (!bufferCoordinates) + { + higherCoord = ScreenToBufferLine(higherCoord, GetLineRendition(higherCoord.Y)); + lowerCoord = ScreenToBufferLine(lowerCoord, GetLineRendition(lowerCoord.Y)); + } + + til::inclusive_rect asRect = { higherCoord.X, higherCoord.Y, lowerCoord.X, lowerCoord.Y }; + _ExpandTextRow(asRect); + higherCoord.X = asRect.Left; + higherCoord.Y = asRect.Top; + lowerCoord.X = asRect.Right; + lowerCoord.Y = asRect.Bottom; + + textSpans.emplace_back(higherCoord, lowerCoord); } + return textSpans; } @@ -1877,10 +1912,23 @@ size_t TextBuffer::SpanLength(const til::point coordStart, const til::point coor // - Just the text. std::wstring TextBuffer::GetPlainText(const til::point& start, const til::point& end) const { - const auto& buffer = _activeBuffer(); - const auto rects = buffer.GetTextRects(start, end, false, true); - const auto textData = buffer.GetText(false, false, rects, nullptr, false); - return textData.text; + std::wstring text; + auto spanLength = SpanLength(start, end); + text.reserve(spanLength); + + auto it = GetCellDataAt(start); + + for (; it && spanLength > 0; ++it, --spanLength) + { + const auto& cell = *it; + if (!cell.DbcsAttr().IsTrailing()) + { + const auto chars = cell.Chars(); + text.append(chars); + } + } + + return text; } // Routine Description: diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index a108b200e60..64c4976af19 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -910,7 +910,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto matchModeStr = winrt::hstring{}; if (MatchMode() == Core::MatchMode::All) { - matchModeStr = fmt::format(std::wstring_view{ L", {}" }, RS_(L"ColorSelection_allMatches")); // ", all matches" + matchModeStr = fmt::format(L", {}", RS_(L"ColorSelection_allMatches")); // ", all matches" } const auto foreground = Foreground(); From faa62b9560ca3d88a8e3a143b161fdd5eb4d1e0c Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 31 Aug 2022 15:33:52 -0700 Subject: [PATCH 12/13] format --- src/cascadia/TerminalApp/AppActionHandlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index a8364f4cbbe..18cae0d5262 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1171,7 +1171,7 @@ namespace winrt::TerminalApp::implementation } } } - + void TerminalPage::_HandleExpandSelectionToWord(const IInspectable& /*sender*/, const ActionEventArgs& args) { From d22aea22c8c359ca04a87ccabdf51a512b66147e Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 31 Aug 2022 16:21:12 -0700 Subject: [PATCH 13/13] why do I keep breaking this PR --- .../TerminalSettingsModel/Resources/en-US/Resources.resw | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index da97974b535..09998b9093f 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -654,9 +654,11 @@ bright white A color used in the "ColorSelection" action. + Expand selection to word + Close all other panes