Skip to content

Add support for Insert-Replace Mode (IRM) and SM/RM - #8689

Closed
Dustin L. Howett (DHowett) wants to merge 6 commits into
mainfrom
dev/duhowett/irm
Closed

Dustin L. Howett (DHowett) wants to merge 6 commits into
mainfrom
dev/duhowett/irm

Conversation

@DHowett

@DHowett Dustin L. Howett (DHowett) commented Jan 2, 2021 •

Copy link
Copy Markdown
Member

This pull request adds support for IRM. I chose a brute-force strategy:
every time we insert text into a row, back up the characters and
attributes and stamp them down at the end of the inserted range.

Pros:

  • Easy
  • Fast implementation
  • Relies on preexisting DBCS lead/trail calculation and pre-formed
    CharRowCells

Cons:

  • Applies brute force
  • Expensive in terms of allocations, especially when inserting only a
    single character. Most output inserted is going to be on the order
    of single characters. 120 single characters, inserted one by one,
    with a memcpy() after each one.

I consider the cons to be acceptable enough for this scenario. It's rare,
and it seems like the most common use is in interactive shells where we
need to operate at "user speed". We can always optimize it later.

I chose to give SCREEN_INFORMATION knowledge of whether it was in
insert mode, rather than the text buffer, adapter or any other
component. When insert mode is on, the text buffer is instructed to
insert--a request it passes directly on to ROW. I've tried to keep
branch misprediction to a minimum (with some optimizer hints) and reduce
default-case allocations¹.

¹ by using unique_ptr<...> to store copied CharRow and ATTR_ROW,
s.t. there is no cost in the normal case except 2*sizeof(void*) bytes
on the stack in ROW::WriteCells

Closes #1947

Validation

  • Tests for IRM must be added
  • Tests for cool new ATTR_ROW stuff added, pass

@ghost ghost added Area-VT Virtual Terminal sequence support Issue-Task It's a feature request, but it doesn't really need a major design. Product-Conhost For issues in the Console codebase labels Jan 2, 2021
Comment thread src/buffer/out/Row.cpp
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
const auto iteratorEmitsText = it && it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly;
insert = insert && iteratorEmitsText; // we only support insert mode for textful iterators

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

CAVEAT: we should be able to insert attributes, but... nobody has asked for this, so why bother?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if by attribute you mean blink, underline, bold, double wide, or double height, then it's what the ANSI terminal protocol requires, and waiting for somebody to ask for it is probably the wrong approach.

if by attribute you mean something internal that doesn't impact ANSI compatibility, then i'd agree, "why bother?"

see also: https://invisible-island.net/vttest/vttest.html

@DHowett Dustin L. Howett (DHowett) Jan 2, 2021 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Nope, this is an implementation detail.

This writer supports text, attributes (ANSI as well as Windows console-specific flags), and combined text+attributes. Attribute-only writing changes the style of text without changing the text itself, and is used to support some Win32 console APIs such as FillConsoleOutputAttribute.

Since there’s no need for us to do an insert operation on attributes only without simultaneously manipulating the text stored in the buffer, we can avoid the complexity. It would produce an interesting effect, but I am not aware of any VT or Console requirement to do so.

Comment thread src/buffer/out/Row.cpp
// Return Value:
// - iterator to first cell that was not written to this row.
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight)
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight, bool insert)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

DESIGN QUESTION: Should insertion be a property on the OutputCellIterator instead of forced down into the row?

Comment thread src/buffer/out/Row.cpp
Comment on lines +113 to +114
std::unique_ptr<CharRow> backupCharRow;
std::unique_ptr<ATTR_ROW> backupAttrRow;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

NOTE: Memory profiler shows this causes 0 allocations, but it does incur a minor stack cost.

Comment thread src/host/screenInfo.cpp
// - will throw exception on error.
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it)
{
__assume(!_insertMode);

@DHowett Dustin L. Howett (DHowett) Jan 2, 2021 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

NOTE: This did nothing in my testing, but it makes me feel better. The optimizer actually deleted this function and TextBuffer::Write (and ...::Insert (!)) because it saw right through our trick. It lifted the boolean load from SCREEN_INFORMATION::_insertMode directly into WriteCharsLegacy and passed it down into the fully-realized ROW::Write(..., insert) (!)

@vixie

Copy link
Copy Markdown

in case my earlier comment on one of the individual changes isn't noticed there, i'll repeat something here:

i hope that this PR will be gated by successful completion of the vttest suite, including its insert/delete character/line tests.

https://invisible-island.net/vttest/vttest.html

@DHowett

Copy link
Copy Markdown
Member Author

vttest (paul vixie (@vixie))

of course! 😄

@DHowett

Dustin L. Howett (DHowett) commented Jan 2, 2021 •

Copy link
Copy Markdown
Member Author

Sorry, now that I'm at a computer I can explain a bit better.

Attributes and attribute insertion

The console buffer implementation (shared with Windows Terminal) stores text and "attributes" separately. Attributes are everything you'd expect--graphic renditions as well as cell size (potentially; pending #8664)--and some things you wouldn't, like whether there's an OSC 8 hyperlink or what "grid lines"² are enabled.

Today, the Write method where the bulk of the changes were made supports overstriking text with text, attributes with attributes, or entire cells with text+attributes. This is the backbone for FillConsoleOutputCharacter (writes text only) and FillConsoleOutputAttribute (writes attributes only), and it also handles every other write operation (which is usually both text and attributes simultaneously.)

The caveat noted above is for the following scenario:

ORIGINAL
| Hello World                |
| RED  | GREEN               |
0                           29

Insert attributes only: BLUE (length=5); position 0

NEW
| Hello World                |
|BLUE | RED  | GREEN         |
0                           29

Since it only changes the attributes, " Hello" would remain, but the colors would shift underneath it such that " Hell" was in blue and "o World" was in red. Nothing in the literature suggests that we'll need to support it. Every change to the buffer coming from VT is accompanied by text.

² The Win32 console supports drawing borders around every cell; the bottom border is distinct from underline, the top border is recycled for overline, and the left/right borders are used in some CJK composition scenarios.

vttest

image

image

edit: thanks for reviewing!

@DHowett

Copy link
Copy Markdown
Member Author

This implementation will absolutely hork UnicodeStorage. It tracks non-dbcs high unicode by row/column index. Shuffling the backing store for the row but not updating UnicodeStorage will make it blast into outer space.

We are likely overdue for a UnicodeStorage redesign.

@DHowett Dustin L. Howett (DHowett) mentioned this pull request Jan 12, 2021
13 of 15 tasks

@vixie paul vixie (vixie) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

i don't have a Windows software build environment, so i'm not in a position to test this PR. is there a .EXE i can try?

@DHowett

Copy link
Copy Markdown
Member Author

paul vixie (@vixie) sorry, we ended up running into some of our longstanding code rot and this work is, as a result, a bit delayed. I'm keeping it on the docket for 2021 such that we can finally put it to bed, and will keep this thread updated.

Once we're in a place where what you test is close to what we want to ship, I'll share a build of conhost/openconsole (without any of the terminal bits) to test with.
d

@github-actions

This comment has been minimized.

@hellow554

Copy link
Copy Markdown

What happened to that poor bot?

@zadjii-msft

Copy link
Copy Markdown
Member

Marcel Hellwig (@hellow554) This branch is pretty stale, I'm betting the spellcheck rules in this branch don't quite work for the updated version of the bot

@WSLUser

WSLUser commented Jun 29, 2022 •

Copy link
Copy Markdown
Contributor

Any chance this sequence will be updated with the latest changes? I think this would be a useful sequence to support.

@DHowett

Copy link
Copy Markdown
Member Author

So, the significant blocker for IRM is that we didn't have a good way to shift our backing store character-by-character to handle insertion (due to some legacy baggage that we had accumulated over time ("UnicodeStorage", which is a hash map of coordinate positions to non-UCS-2 code units)). That's being actively worked on in #8000. Once that lands, we can dust this off.

@zadjii-msft

Copy link
Copy Markdown
Member

Dustin L. Howett (@DHowett) since the implementation of this is likely to change aggressively after #8000 lands, should we close out this draft for now?

@DHowett

Copy link
Copy Markdown
Member Author

Nah, you're totally right. I can't land this in its current form, and it's just clogging the list up until it can land. I'll keep the branch.

@vixie

paul vixie (vixie) commented Oct 11, 2022 via email

Copy link
Copy Markdown

@vixie

Copy link
Copy Markdown

given this has been open for more than two years and that it's blocked on #8000 which has also been open for two years, can i ask that the TERM variable be set to something other than "xterm-256color" please? that's false, since xterm has insert mode, and this terminal emulator does not. if you won't change it to "vt220" then at least add a configuration setting for TERM. you can revert this when and if this terminal emulator ever actually becomes xterm compatible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-VT Virtual Terminal sequence support Issue-Task It's a feature request, but it doesn't really need a major design. Product-Conhost For issues in the Console codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for VT102 Insertion-Replace Mode (IRM)

5 participants