--- .title = "Editor", .date = @date("2025-10-19T00:00:00"), .author = "Igor Támara", .layout = "tutorial.shtml", .draft = true, .custom = { .githubedit = "docs/architecture/editor.smd", .codepath = "src/tui/editor.zig", }, --- To get the most of this section, it's recommended to have read the [architecture briefing](/docs/architecture), about [commands](/docs/architecture/command) and [keybinds](/docs/architecture/keybind). []($section.id("concepts")) ## Some concepts The [editor](#editor_concept) coordinates visualization and modification of buffer contents, multiple cursors, selections and marks. We will delve in editor concepts, buffer inner manipulation with ropes is not covered here. []($section.id("tui_editor")) ### Editor and TUI During this section we will refer to the concept of the editor as the one capable of modifying buffer contents, the visible gutters and line numbers as other terminal user interface (TUI) is covered in another chapter. []($section.id("view_concept")) ### View `View` holds the information about the area of the buffer that is currently visible in the screen by the user. Is related to the [primary cursor](#cursor_concept). []($section.id("cursor_concept")) ### Cursor The `primary Cursor` holds a position in the Buffer, the `Editor` makes the link between both of them, signaling the part of the `Buffer` that can be modified and manipulated as you see it. It scrolls on the current visible portion [view](#view_concept) of the buffer, when manipulated with the keyboard, the mouse can change the move the view while the primary mouse is offscreen; when keystrokes arrive to the editor, the view focuses to the primary cursor to make it visible and available to be used. Flow has multiple cursors each one holding the relative position to the buffer with `row`, `col` and `target`, the last one used to make the cursor jump to a possibly next movement,for example, when moving between lines, this is a way to "remember" where to jump back. When creating multiple cursors they signal many buffer places and a subset is seen inside the [view](#view_concept). Cursors visibility depends on the size of both the buffer contents and the editor in your device. Most of editors operations act on the set of CurSels and the Primary Cursor is a [CurSel](#cursel_concept). []($section.id("selection_concept")) ### Selection A selection is represented by begin and end [cursors](#cursor_concept) and offers basic functions that will consitute the changes needed with deletions, insert replacements handled by the[editor](#tui_editor) services and [commands](/docs/architecture/command). A `Selection` has two cursors that are not visible, they mark the begin and the end of the selection. []($section.id("cursel_concept")) ### CurSel The CurSel is what is presented to user, holding a [cursor](#cursor_concept) and optionally a [selection](#selection_concept). []($section.id("mark_concept")) ### Mark what allow to have the concept of a cursor with a selection. A `Cursel` is composed by a cursor and optionally a Selection. To complete the editor scenario, `Marks` have the potential to become selections; the marks become evident to the eye when in search mode, they are seen as the primary cursor is positioned over an occurrence with a different color according to the theme. []($section.id("editor_concept")) ### Editor The Editor will be acting on Buffer.Root which is the root of the tree representing the document that is being edited. API Buffer.Root is stable and offers the necessary to insert, delete and move along the buffer, knowing if the end or the beginning of the document have been reached when interacting with a Cursor. Cursors, Selections and Cursels don't know about the buffer, and they need to receive a reference to them to have positions and also sometimes receive metrics from the editor that help determine if a cursor is about to exit boundaries of the buffer and have everything "in place". []($section.id("commands")) ## Editor Commands We mentioned earlier that most of the operations work on all the cursors and selections, moreover, there are various commands acting over the set of cursors, selections, cursels or marks. Given said this, we will be using functions as parameters in most of the situations. Functional programming languages are popular in these scenarios, to name a prominent one, Emacs and emacs lisp. If the buffer is not to be modified, we will be using the method `buf_root` to get the root of the buffer to find and position the cursors. In the other hand, we will use `buf_for_update` when the buffer is to be modified. The benefit of sending functions as parameters is that code is reused and the codebase can grow without much sacrifice when adding new functionalities, because one would be thinking only in the current cursor and if required, the operation will be applied to all the cursors, the same applies to selections, cursels and marks. []($section.id("moving")) ## Moving For example, to move the cursors a page up, we can look at the command `move_page_up`, which uses the method `with_cursors_and_view_const` sending the function `move_cursor_page_up`. Looking inside `with_cursors_and_view_const`, it iterates over all the cursors and invokes `with_cursor_and_view_const`, sending a cursor as a parameter, that function, will invoke `move_cursor_page_up` whose commitment is to use the method `move_page_up` from the cursor, sending the view and the metrics of the editor. The family of `move` functions is big, look for the methods whose name has the word move in them. with the command palette is possible to get a glimpse of them. []($section.id("selecting")) ## Selections There are naming conventions for the functions that help understanding what each one is doing, there has been taken great deal of care to maintain consistency that needs to be followed to make it easier to continue extending on functionalities. Look at the following set of functions, all of them act on cursors, cursels and selections, in singular and plural, and some of them receive arguments, the repeat functions act many times over the set of the group that they are intended to do so. The parameters and following the calls from one to another will let you navigate and get the hang of their usage. []($section.id("modifying")) ## Modifying the buffer The `select` family of functions is bigger than the set of `move` functions, in contrast, the `cut`, `delete`, `insert`, `paste` looks smaller, and this is accomplished making composition of functions. Usually when modifying something, first there is a process to locate the cursor, cursel o selection in the proper place and then applying the modification. [Discord](https://discord.com/invite/4wvteUPphx) and [Github issues](https://github.com/neurocyte/flow/issues) are the main channels to do so. ## Helper function When creating commands and services for the editor, sometimes is handy where are the cursels. It's possible to use `log_cursel`: >[]($block.attrs('line-numbers-js')) >```zig >pub fn log_cursel(self: *Self, cursel: CurSel) void { > if (cursel.selection) |sel| { > self.logger.print("[{}:{}.{}] {}:{}.{} - {}:{}.{}", .{ > cursel.cursor.row, cursel.cursor.col, cursel.cursor.target, > sel.begin.row, sel.begin.col, sel.begin.target, > sel.end.row, sel.end.col, sel.end.target, > }); > } else { > self.logger.print("[{}:{}.{}] no selection", .{ > cursel.cursor.row, > cursel.cursor.col, > cursel.cursor.target > }); > } >} >``` []($section.id("next")) ## Next steps * [minimodes](/docs/architecture/minimode) invoke [commands](/docs/architecture/minimode) defined in the editor * [palettes](/docs/architecture/palette) are open by commands and when selected an item, a command is invoked * Plenty of [commands](/docs/architecture/command) are defined in the editor * [Passing parameters](/docs/architecture/inner_data_exchange) between commands and functions