Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSC 8 support for conhost and terminal #7251

Merged
merged 35 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
16720a0
osc 8 support for conhost and terminal
PankajBhojwani Aug 11, 2020
51bd477
Correctly gets buffer position on click now
PankajBhojwani Aug 11, 2020
d268322
small fixes
PankajBhojwani Aug 12, 2020
3090b1c
more small fixes
PankajBhojwani Aug 12, 2020
b81014e
Merge branch 'master' of https://github.com/microsoft/terminal into d…
PankajBhojwani Aug 12, 2020
015fb8f
tryna pass pipeline checks
PankajBhojwani Aug 12, 2020
4a74f83
send event upon hyperlink click, allow custom ids, remove obsolete re…
PankajBhojwani Aug 15, 2020
049ee61
text buffer tests
PankajBhojwani Aug 17, 2020
c18f32a
spell
PankajBhojwani Aug 17, 2020
bd74eff
output engine test
PankajBhojwani Aug 17, 2020
0327750
more tests
PankajBhojwani Aug 17, 2020
405066e
resolve conflict
PankajBhojwani Aug 17, 2020
5995275
fixes
PankajBhojwani Aug 18, 2020
409b358
some requested changes
PankajBhojwani Aug 18, 2020
dfc8771
more changes
PankajBhojwani Aug 18, 2020
c28d2a6
conflict resolution
PankajBhojwani Aug 19, 2020
8a105d7
bug fix
PankajBhojwani Aug 19, 2020
7e96737
small changes
PankajBhojwani Aug 21, 2020
e19fbe5
fixed hyperlink not working after resize
PankajBhojwani Aug 21, 2020
bc70439
fixes
PankajBhojwani Aug 21, 2020
9d65b32
noexcept
PankajBhojwani Aug 21, 2020
aefb2e0
more noexcept
PankajBhojwani Aug 21, 2020
d0de3d5
except
PankajBhojwani Aug 21, 2020
eb3cda5
optimize prune hyperlinks
PankajBhojwani Aug 21, 2020
63723e8
hyperlinks is not a recognized word??
PankajBhojwani Aug 21, 2020
01928d3
check ctrl first then shift
PankajBhojwani Aug 24, 2020
121f51c
fuzzer
PankajBhojwani Aug 24, 2020
52e5743
fix
PankajBhojwani Aug 24, 2020
d1b0e83
terminator, blank space fixes
PankajBhojwani Aug 25, 2020
82cc197
ctrl+shift when no uri updates selection
PankajBhojwani Aug 26, 2020
4ecbfc0
addressing comments
PankajBhojwani Aug 27, 2020
b75f189
SGR 0 fixes
PankajBhojwani Aug 27, 2020
5dcd706
id resolution
PankajBhojwani Aug 27, 2020
a099fc7
added roundtrip test
PankajBhojwani Aug 28, 2020
5620c01
spell
PankajBhojwani Aug 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 47 additions & 41 deletions src/buffer/out/textBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,47 +552,8 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode)
// to the logical position 0 in the window (cursor coordinates and all other coordinates).
_renderTarget.TriggerCircling();

// First, check the old first row for hyperlink references
// If there are any, search the entire buffer for the same reference
// If the buffer does not contain the same reference, we can remove that hyperlink from our map
// This way, obsolete hyperlink references are cleared from our hyperlink map instead of hanging around
std::unordered_set<USHORT> refs;
const auto firstAttrRow = _storage.at(_firstRow).GetAttrRow();
// Get all the hyperlink references in the row we're erasing
for (auto it = firstAttrRow.begin(); it != firstAttrRow.end(); ++it)
{
if (it->IsHyperlink())
{
refs.emplace(it->GetHyperlinkId());
}
}
if (!refs.empty())
{
const auto total = TotalRowCount();
// Loop through all the rows in the buffer
for (size_t i = 1; i != total; ++i)
{
const auto attrRow = GetRowByOffset(i).GetAttrRow();
for (auto it = attrRow.begin(); it != attrRow.end(); ++it)
{
if (it->IsHyperlink() && (refs.find(it->GetHyperlinkId()) != refs.end()))
{
refs.erase(it->GetHyperlinkId());
}
}
if (refs.empty())
{
// No more hyperlink references left to search for, terminate early
break;
}
}
}

// Now delete obsolete references from our map
for (auto it = refs.begin(); it != refs.end(); ++it)
{
RemoveHyperlinkFromMap(*it);
}
// Prune hyperlinks to delete obsolete references
_PruneHyperlinks();

// Second, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed.
auto fillAttributes = _currentAttributes;
Expand Down Expand Up @@ -1228,6 +1189,51 @@ const COORD TextBuffer::_GetWordEndForSelection(const COORD target, const std::w
return result;
}

void TextBuffer::_PruneHyperlinks()
{
// First, check the old first row for hyperlink references
// If there are any, search the entire buffer for the same reference
// If the buffer does not contain the same reference, we can remove that hyperlink from our map
// This way, obsolete hyperlink references are cleared from our hyperlink map instead of hanging around
std::unordered_set<USHORT> refs;
const auto firstAttrRow = _storage.at(_firstRow).GetAttrRow();
// Get all the hyperlink references in the row we're erasing
for (auto it = firstAttrRow.begin(); it != firstAttrRow.end(); ++it)
{
if (it->IsHyperlink())
{
refs.emplace(it->GetHyperlinkId());
}
}
if (!refs.empty())
{
const auto total = TotalRowCount();
// Loop through all the rows in the buffer
for (size_t i = 1; i != total; ++i)
{
const auto attrRow = GetRowByOffset(i).GetAttrRow();
for (auto it = attrRow.begin(); it != attrRow.end(); ++it)
{
if (it->IsHyperlink() && (refs.find(it->GetHyperlinkId()) != refs.end()))
{
refs.erase(it->GetHyperlinkId());
}
}
if (refs.empty())
{
// No more hyperlink references left to search for, terminate early
break;
}
}
}

// Now delete obsolete references from our map
for (auto it = refs.begin(); it != refs.end(); ++it)
{
RemoveHyperlinkFromMap(*it);
}
}

// Method Description:
// - Update pos to be the position of the first character of the next word. This is used for accessibility
// Arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/buffer/out/textBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ class TextBuffer final
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;

void _PruneHyperlinks();

#ifdef UNIT_TESTING
friend class TextBufferTests;
friend class UiaTextRangeTests;
Expand Down
17 changes: 12 additions & 5 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_terminal->SetSelectionEnd(terminalPosition, mode);
_selectionNeedsToBeCopied = true;
}
if (ctrlEnabled && mode == ::Terminal::SelectionExpansionMode::Cell)
if (ctrlEnabled && multiClickMapper == 1)
{
// Control+Click: if the text selected is a hyperlink, open the link
const auto uri = _terminal->GetHyperlink(terminalPosition);
Expand Down Expand Up @@ -2853,11 +2853,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - The uri
void TermControl::_HyperlinkHandler(std::wstring uri)
{
auto parsed = winrt::Windows::Foundation::Uri(uri);
if (parsed.SchemeName() == L"http" || parsed.SchemeName() == L"https")
try
{
auto parsed = winrt::Windows::Foundation::Uri(uri);
if (parsed.SchemeName() == L"http" || parsed.SchemeName() == L"https")
{
auto hyperlinkArgs = winrt::make_self<OpenHyperlinkEventArgs>(winrt::hstring(uri));
_openHyperlinkHandlers(*this, *hyperlinkArgs);
}
}
catch (...)
{
auto hyperlinkArgs = winrt::make_self<OpenHyperlinkEventArgs>(winrt::hstring(uri));
_openHyperlinkHandlers(*this, *hyperlinkArgs);
LOG_HR(wil::ResultFromCaughtException());
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,21 @@ bool Terminal::IsTrackingMouseInput() const noexcept
return _terminalInput->IsTrackingMouseInput();
}

// Method Description:
// - If the clicked text is a hyperlink, open it
// Arguments:
// - The position of the clicked text
std::wstring Terminal::GetHyperlink(const COORD position)
{
auto attr = _buffer->GetCellDataAt(_ConvertToBufferCell(position))->TextAttr();
if (attr.IsHyperlink())
{
auto uri = _buffer->GetHyperlinkUriFromId(attr.GetHyperlinkId());
return uri;
}
return L"";
}

// Method Description:
// - Send this particular (non-character) key event to the terminal.
// - The terminal will translate the key and the modifiers pressed into the
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class Microsoft::Terminal::Core::Terminal final :

void TrySnapOnInput() override;
bool IsTrackingMouseInput() const noexcept;

std::wstring GetHyperlink(const COORD position);
#pragma endregion

#pragma region IBaseData(base to IRenderData and IUiaData)
Expand Down Expand Up @@ -194,7 +196,6 @@ class Microsoft::Terminal::Core::Terminal final :
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansionMode> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
std::wstring GetHyperlink(const COORD position);

const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const;
#pragma endregion
Expand Down
15 changes: 0 additions & 15 deletions src/cascadia/TerminalCore/TerminalSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,18 +286,3 @@ void Terminal::ColorSelection(const COORD, const COORD, const TextAttribute)
{
THROW_HR(E_NOTIMPL);
}

// Method Description:
// - If the clicked text is a hyperlink, open it
// Arguments:
// - The position of the clicked text
std::wstring Terminal::GetHyperlink(const COORD position)
{
auto attr = _buffer->GetCellDataAt(_ConvertToBufferCell(position))->TextAttr();
if (attr.IsHyperlink())
{
auto uri = _buffer->GetHyperlinkUriFromId(attr.GetHyperlinkId());
return uri;
}
return L"";
}
21 changes: 21 additions & 0 deletions src/host/getset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,27 @@ void DoSrvSetCursorColor(SCREEN_INFORMATION& screenInfo,
screenInfo.GetActiveBuffer().GetTextBuffer().GetCursor().SetColor(cursorColor);
}

void DoSrvAddHyperlink(SCREEN_INFORMATION& screenInfo,
const std::wstring_view uri,
const std::wstring_view params)
{
auto attr = screenInfo.GetAttributes();
if (uri.empty())
{
// URI is empty, this means we are ending a hyperlink
attr.SetHyperlinkId(0);
screenInfo.GetTextBuffer().SetCurrentAttributes(attr);
}
else
{
// URI is non-empty, this means we are starting a hyperlink
USHORT id = screenInfo.GetTextBuffer().GetHyperlinkId(params);
attr.SetHyperlinkId(id);
screenInfo.GetTextBuffer().SetCurrentAttributes(attr);
screenInfo.GetTextBuffer().AddHyperlinkToMap(uri, id);
}
}

// Routine Description:
// - A private API call for forcing the renderer to repaint the screen. If the
// input screen buffer is not the active one, then just do nothing. We only
Expand Down
4 changes: 4 additions & 0 deletions src/host/getset.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ void DoSrvSetCursorStyle(SCREEN_INFORMATION& screenInfo,
void DoSrvSetCursorColor(SCREEN_INFORMATION& screenInfo,
const COLORREF cursorColor);

void DoSrvAddHyperlink(SCREEN_INFORMATION& screenInfo,
const std::wstring_view uri,
const std::wstring_view params);

void DoSrvPrivateRefreshWindow(const SCREEN_INFORMATION& screenInfo);

[[nodiscard]] HRESULT DoSrvSetConsoleOutputCodePage(const unsigned int codepage);
Expand Down
16 changes: 1 addition & 15 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,20 +780,6 @@ bool ConhostInternalGetSet::PrivateIsVtInputEnabled() const
// - true
bool ConhostInternalGetSet::PrivateAddHyperlink(const std::wstring_view uri, const std::wstring_view params) const
{
auto attr = _io.GetActiveOutputBuffer().GetAttributes();
if (uri.empty())
{
// URI is empty, this means we are ending a hyperlink
attr.SetHyperlinkId(0);
_io.GetActiveOutputBuffer().GetTextBuffer().SetCurrentAttributes(attr);
}
else
{
// URI is non-empty, this means we are starting a hyperlink
USHORT id = _io.GetActiveOutputBuffer().GetTextBuffer().GetHyperlinkId(params);
attr.SetHyperlinkId(id);
_io.GetActiveOutputBuffer().GetTextBuffer().SetCurrentAttributes(attr);
_io.GetActiveOutputBuffer().GetTextBuffer().AddHyperlinkToMap(uri, id);
}
DoSrvAddHyperlink(_io.GetActiveOutputBuffer(), uri, params);
return true;
}
2 changes: 1 addition & 1 deletion src/renderer/vt/VtSequences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ using namespace Microsoft::Console::Render;
// - The hyperlink URI
// Return Value:
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT VtEngine::_SetHyperlink(std::wstring uri, USHORT id) noexcept
[[nodiscard]] HRESULT VtEngine::_SetHyperlink(const std::wstring& uri, const USHORT& id) noexcept
{
if (uri.empty())
{
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/vt/vtrenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _SetInvisible(const bool isInvisible) noexcept;
[[nodiscard]] HRESULT _SetCrossedOut(const bool isCrossedOut) noexcept;
[[nodiscard]] HRESULT _SetReverseVideo(const bool isReversed) noexcept;
[[nodiscard]] HRESULT _SetHyperlink(const std::wstring uri, USHORT id) noexcept;
[[nodiscard]] HRESULT _SetHyperlink(const std::wstring& uri, const USHORT& id) noexcept;

[[nodiscard]] HRESULT _RequestCursor() noexcept;

Expand Down