The rendering flow

Understanding the rendering flow is important for understanding how Container and UIControl objects interact. We will demonstrate it by explaining the flow around a BufferControl.


A BufferControl is a UIControl for displaying the content of a Buffer. A buffer is the object that holds any editable region of text. Like all controls, it has to be wrapped into a Window.

Let’s take the following code:

from prompt_toolkit.enums import DEFAULT_BUFFER
from prompt_toolkit.layout.containers import Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.buffer import Buffer

b = Buffer(name=DEFAULT_BUFFER)

What happens when a Renderer objects wants a Container to be rendered on a certain Screen?

The visualisation happens in several steps:

  1. The Renderer calls the write_to_screen() method of a Container. This is a request to paint the layout in a rectangle of a certain size.

    The Window object then requests the UIControl to create a UIContent instance (by calling create_content()). The user control receives the dimensions of the window, but can still decide to create more or less content.

    Inside the create_content() method of UIControl, there are several steps:

    1. First, the buffer’s text is passed to the lex_document() method of a Lexer. This returns a function which for a given line number, returns a “formatted text list” for that line (that’s a list of (style_string, text) tuples).
    2. This list is passed through a list of Processor objects. Each processor can do a transformation for each line. (For instance, they can insert or replace some text, highlight the selection or search string, etc…)
    3. The UIControl returns a UIContent instance which generates such a token lists for each lines.

The Window receives the UIContent and then:

  1. It calculates the horizontal and vertical scrolling, if applicable (if the content would take more space than what is available).
  2. The content is copied to the correct absolute position Screen, as requested by the Renderer. While doing this, the Window can possible wrap the lines, if line wrapping was configured.

Note that this process is lazy: if a certain line is not displayed in the Window, then it is not requested from the UIContent. And from there, the line is not passed through the processors or even asked from the Lexer.