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

Make UTextFromTextBuffer newline aware #17120

Merged
merged 3 commits into from Apr 29, 2024
Merged

Conversation

lhecker
Copy link
Member

@lhecker lhecker commented Apr 24, 2024

This PR achieves two things:

  • When encountering rows with newlines (WasForceWrapped = false)
    we'll now copy the contents out of the row and append a \n.
    To make utext_clone cheap, it adds a reference counted buffer.
  • Text extraction in Terminal::GetHyperlinkAtBufferPosition
    was fixed by using a higher level TextBuffer::GetPlainText
    instead of iterating through each cell.

Closes #16676
Closes #17065

Validation Steps Performed

  • In pwsh execute the following:
    "`e[999C`e[22Dhttps://example.com/foo`nbar"
  • Hovering over the URL only underlines .../foo and not bar
  • The tooltip ends in .../foo and not .../fo

}

return text;
const auto req = CopyRequest::FromConfig(*this, start, end, true, false, false, false);
Copy link
Member Author

@lhecker lhecker Apr 24, 2024

Choose a reason for hiding this comment

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

FWIW I believe we should remove the constructor from CopyRequest and instead sanitize it in each individual function that accepts it. This would allow the callers to use designated initializers like so:

CopyRequest req{
    .beg = ...,
    .end = ...,
    .singleLine = true,
};

This would turn CopyRequest into a true request struct IMO. It would be similar to sanitizing a web request on the server to protect against malicious clients.


return text;
const auto req = CopyRequest::FromConfig(*this, start, end, true, false, false, false);
return GetPlainText(req);
Copy link
Member

Choose a reason for hiding this comment

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

are we sure this is functionally equivalent? can you test the known multi-cell-character URL bugs?

Copy link
Member Author

@lhecker lhecker Apr 24, 2024

Choose a reason for hiding this comment

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

GetPlainText is only used when pressing Ctrl+Enter on a URL (in mark mode) if it's detected via pattern, and the pattern only supports ASCII characters.

src/buffer/out/UTextAdapter.cpp Show resolved Hide resolved
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

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

Clever. Effectively, you're letting our utext adapter fall back to a temporary buffer that is filled with the content of one row plus a newline character.

My concern is, most of the text in a terminal is like this (not forced wrap). Is it possible to have a fast path for when nobody is trying to read the end of the line?

@lhecker
Copy link
Member Author

lhecker commented Apr 24, 2024

I don't think it's worth special-casing it at this time. The copy is very fast and not a major bottleneck. I tried storing the \n inside the ROW string (in place of the force-wrap flag) and unfortunately that doesn't work because of our _doubleBytePadded flag.

size_t capacity;
wchar_t data[1];

static RefcountBuffer* EnsureCapacityForOverwrite(RefcountBuffer* buffer, size_t capacity)
Copy link
Member

Choose a reason for hiding this comment

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

nit: This might warrant a doc comment. This is the first thing in the review, and it's not immediately clear what it's trying to achieve

check if buffer exists, and is at least size capacity. If it isn't, make a new one at least the size of capacity.

}

const auto oldCapacity = buffer ? buffer->capacity << 1 : 0;
const auto newCapacity = std::max(capacity + 128, oldCapacity);
Copy link
Member

Choose a reason for hiding this comment

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

if we had to make a new buffer, make the new buffer's size the bigger of (2x the old buffer's size OR capacity+128). I guess to add some padding, in case the caller only needed 80 right now, but might want 200 with this same buffer

constexpr RefcountBuffer*& accessBuffer(UText* ut) noexcept
{
static_assert(sizeof(ut->q) == sizeof(RefcountBuffer*));
return *std::bit_cast<RefcountBuffer**>(&ut->q);
Copy link
Member

Choose a reason for hiding this comment

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

oh so this is some spooky bit casting magic, got it

@DHowett DHowett added this pull request to the merge queue Apr 29, 2024
@zadjii-msft zadjii-msft added this to the Terminal v1.21 milestone Apr 29, 2024
Merged via the queue into main with commit 5b8eadb Apr 29, 2024
20 checks passed
@DHowett DHowett deleted the dev/lhecker/17065-search-newlines branch April 29, 2024 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

URL highlight broken when URL ends at the EOL Resizing sometimes make links unclickable
3 participants