about summary refs log tree commit diff
path: root/tests.py
Commit message (Collapse)AuthorAgeFilesLines
* Document tests and make them easier to runsterni2021-07-151-5/+16
| | | | | | | | * Add a shebang and make the tests.py skript executable * Document how to run the tests in the README * Add docstrings to the tests (which are displayed with -v)
* Implement a reasonable subset of SGR escape sequencessterni2021-07-151-0/+28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | SGR (Select Graphical Representation) escape sequences are CSI escape sequences ending in the final byte 'm'. They are described in the ECMA-48 standard, but we also support a compatible extension which is commonly used nowadays, namely extended colors (256 colors and true 24 bit colors) which are specified in ITU-T Rec. T.416. SGR sequences are probably the most commonly used escape sequences and a lot of CLI tools now feel much more familiar in saneterm. The implemented SGR sequences for example allow: * to change the current text's foreground and background color in the three commonly used color modes 8/16 colors, 256 colors and 24 bit true color. * to change the current text's appearance: italic, bold, underline, strikethrough and more are supported. The current implementation uses a new TextStyle object which is added to pty.Parser's state to track the inherintly stateful changes in text appearance described by SGR escape sequences. When the TextStyle object changes, a TEXT_STYLE event is emitted and the a Gtk.TextTag is created from the TextStyle and registered in the widget's TextBuffer. For the most part this is quite straightforward, just two areas deserve more attention: * The extended colors (256 colors and 24 bit true color) are a bit more complicated to parse which is handled by parse_extended_color(). This function doesn't fully support everything the recommendation mandates. Especially true color will need more real world testing, e. g. lolcat(1) a heavy user of true color doesn't even emit true color escape sequences conforming to the standard. * Color handling in general contributes to most of the complexity: * There are three ways to specify colors via SGR escape sequences we support which all need to be converted to Gdk.RGBA objects. This is handled by saneterm.color.Color. True color is trivial, for 256 colors we implement the conversion instead of generating a lookup table (like XTerm does). For the 8 basic colors in their normal and bright variants, we use hard coded list of X11 color names for now. This probably should become configurable in the future. * Many implementation use the intensity escape sequences to influence color vibrance: SGR 2 is interpreted as dim wrt to colors and SGR 1 not only makes the text bold but also chooses brighter colors. So far we interpret SGR 1, 2 and 22 only in terms of font weight. EMCA-48 permits both. Changing the color intensity as well increases complexity and has little benefit, so this should probably be kept this way. * Instead we implement the 90-97 and 100-107 non-standard bright color SGR escape sequences. The current implementation is, however, not without issues: * Tracking the text style state in the parser is probably a layer violation — pty.Parser should instead translate the escape sequences into events and the state tracking done in saneterm.terminal. * Performance is poor if a lot of escape sequences are in the input. This is due to two reasons: a) insert_data is called with little chunks of text which decreases performance and b) a lot of anonymous Gtk TextTags are created which hurts performance a lot. We should investigate a way to deduplicate the created TextTags (by using names?) and possibly decouple the application of tags from the insertion of text itself.
* pty.Parser: track pos in iterator, support postponing parsingsterni2021-07-151-0/+96
The next step for pty.Parser will be to support parsing ANSI CSI escape sequences which consist of multiple codepoints, but need to be treated as an unit. Thus we need to be able to push some unparsed input to the next invocation of parse(). The way I've chosen to support this is by supporting backtracking in an iterator which wraps the input string. This way we'll be able to keep consuming input from the iterator in the main for loop, but can backtrack in case we hit the end of input or encounter a parse failure. I opted to implement this iterator in the form of an object that tracks an index into a string. For one this feature also helps us in the parser: We need to track the current index anyways and it is also helpful for checking if the end of input has been reached. Additionally this seems like the only feasible way to implement such a backtracking iterator wrapper: The other alternative would've been to copy.deepcopy() the iterator at a point we may want to return to — however this is not practical as deepcopy will hit a recursion depth error with the moderately sized chunks of input we handle in parse(). This commit introduces the new iterator and adapts our parser code for it and lays the groundwork for resuming from unparsed input of a previous invocation of parse(). Additionally it also ships a set of unit tests for the iterator, many methods of which are still unused in the main parser code.