flow-website/content/docs/architecture/editor.smd
2025-11-17 21:27:20 +01:00

214 lines
No EOL
7.9 KiB
Text

---
.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