214 lines
No EOL
7.9 KiB
Text
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 |