Compare commits
10 commits
edf3a4d51a
...
b01dfdb992
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b01dfdb992 | ||
|
|
0494574c36 | ||
|
|
e837a66b97 | ||
|
|
5eedc17542 | ||
|
|
34ec8be355 | ||
|
|
d14b44ed1c | ||
|
|
a7bcd6dd9e | ||
|
|
ca131d2135 | ||
|
|
1964cb7fd6 | ||
|
|
c87d310f7a |
11 changed files with 1023 additions and 45 deletions
|
|
@ -45,7 +45,7 @@ Some [custom frontmatter](https://zine-ssg.io/docs/supermd/#frontmatter) fields
|
|||
Directs to contribute editing this documentation. Used by `layout/tutorial.shtml`.
|
||||
|
||||
```ziggy
|
||||
.githubedit** = "/docs/testing.md",
|
||||
.githubedit** = "/docs/testing.smd",
|
||||
```
|
||||
|
||||
### codepath (Optional)
|
||||
|
|
|
|||
|
|
@ -44,27 +44,32 @@ offers services around the set of projects.
|
|||
[]($section.id("commands"))
|
||||
## Editor commands and modes
|
||||
|
||||
When a buffer is active, it has an `Editor` attached to it; an editor
|
||||
might have associated tree-sitter support, given the file type detected,
|
||||
and offers common services that are aimed to be used by `Commands` to
|
||||
manipulate the contents of a buffer at a higher level, the selections,
|
||||
cursors, cursor selections `CurSel` and the `View`. The commands are
|
||||
used by `Modes` with `Keybindings`. The main mode is Flow and the
|
||||
keybindings can be used to map to a mode built up entirely on solely
|
||||
calling already created commands. An example of a mode created by
|
||||
command composition is `Emacs` mode, for instance, it's possible to
|
||||
create a nano mode with just keybindings. In the other hand, `Vim` and
|
||||
`Helix` modes have particular definitions for commands that interact
|
||||
with the buffers, being modal editors.
|
||||
When a buffer is active, it has an Editor
|
||||
attached to it; an editor might have associated tree-sitter support,
|
||||
given the file type detected, and offers common services that are aimed
|
||||
to be used by `Commands` to manipulate the contents of a buffer at a
|
||||
higher level, the selections, cursors, cursor selections `CurSel` and
|
||||
the `View`. [Commands](/docs/architecture/command) are used by `Modes`
|
||||
with [Keybindings](/docs/architecture/keybind). The main mode is Flow
|
||||
and the keybindings can be used to map to a mode built up entirely on
|
||||
solely calling already created commands. An example of a mode
|
||||
created by command composition is `Emacs` mode, for instance, it's
|
||||
possible to create a nano mode with just keybindings. In the other hand,
|
||||
`Vim` and [Helix](/docs/mode/helix) modes have particular definitions
|
||||
for commands that interact with the buffers, being modal editors.
|
||||
|
||||
[]($section.id("tui"))
|
||||
## Text user interface
|
||||
|
||||
`Tui` governs it all offering support for `Palettes` that are known
|
||||
in other environments as pickers, as well as offering information
|
||||
through a set of `_views` (i.e. `logview`, `inputview`,
|
||||
`inspector_view`) and `status` (i.e. `tabs`, `clock`, `branch`,
|
||||
`linenum`).
|
||||
`Tui` governs it all offering support for
|
||||
[palettes](/docs/architecture/palette) that are known in other
|
||||
environments as pickers, as well as offering information through a
|
||||
set of `_views` (i.e. `logview`, `inputview`, `inspector_view`) and
|
||||
`status` (i.e. `tabs`, `clock`, `branch`, `linenum`), in the statusbar
|
||||
[minimodes](/docs/architecture/minimode) will be present too, those
|
||||
that receive more keypresses to offer additional functionality, such
|
||||
as finding in files, finding in the current buffer, open files
|
||||
and replacing a character.
|
||||
|
||||
[]($section.id("oses"))
|
||||
## Operating systems and UI
|
||||
|
|
@ -77,16 +82,16 @@ and Android via Termux, while in Windows there is an special GUI.
|
|||
## Communication between components
|
||||
|
||||
[Thespian](https://github.com/neurocyte/thespian) is in charge of
|
||||
processes synchronization and allows sending messages between
|
||||
different flow components, for example, when a widget needs
|
||||
updating information from changing states of internal data and
|
||||
when components or external processes take time and need to return
|
||||
an answer, all this without blocking the user interface. Tree-sitter
|
||||
queries to highlight the current file of a particular
|
||||
language and LSPs usually take time by the nature of operations they
|
||||
perform, integration with git and running a `shell` command via a
|
||||
`task` all are coordinated thanks to the infrastructure that
|
||||
Thespian provides.
|
||||
processes synchronization and allows sending
|
||||
[messages between different flow components](/docs/architecture/inner_data_exchange),
|
||||
for example, when a widget needs updating information from changing
|
||||
states of internal data and when components or external processes take
|
||||
time and need to return an answer, all this without blocking the user
|
||||
interface. Tree-sitter queries to highlight the current file of a
|
||||
particular language and LSPs usually take time by the nature of
|
||||
operations they perform, integration with git and running a `shell`
|
||||
command via a `task` all are coordinated thanks to the infrastructure
|
||||
that Thespian provides.
|
||||
|
||||
[]($section.id("languages"))
|
||||
## Programming languages support
|
||||
|
|
@ -111,7 +116,7 @@ feedback via `logview`. To view logs use `f11` to toggle the
|
|||
previous messages, or alternatively, open flow with the option
|
||||
`--show-logs`.
|
||||
|
||||
To log something import
|
||||
To log something, first import
|
||||
|
||||
```zig
|
||||
const log = @import("log");
|
||||
|
|
@ -135,13 +140,17 @@ logger.print("{} unsaved buffer(s) remaining", .{remaining});
|
|||
### View key presses
|
||||
|
||||
There are situations when you press some keys without the expected
|
||||
behavior happening, to review if flow is getting the keys, or your desktop
|
||||
environment or something else are capturing them, you will want to
|
||||
invoke flow with the option `--show-input`.
|
||||
behavior happening, to review if flow is getting the keys, the
|
||||
[keybindings are associated](/docs/architecture/keybind), and are
|
||||
executing the [desired command](/docs/architecture/command), or maybe
|
||||
your desktop environment or something else is capturing them, you will
|
||||
want to invoke flow with the option `--show-input`.
|
||||
|
||||
[]($section.id("next"))
|
||||
## Next steps
|
||||
|
||||
* [Configure some keybinds](/docs/architecture/keybind)
|
||||
* [Guidelines for contributions](/docs/contributing)
|
||||
* [Create your own commands](/docs/architecture/command)
|
||||
* [Take a peek on testing](/docs/testing)
|
||||
* [Back to docs](/docs)
|
||||
176
content/docs/architecture/command.smd
Normal file
176
content/docs/architecture/command.smd
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
---
|
||||
.title = "Commands",
|
||||
.date = @date("2025-10-15T00:00:00"),
|
||||
.author = "Igor Támara",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "/docs/architecture/command.smd",
|
||||
.codepath = "src/tui/editor.zig",
|
||||
},
|
||||
---
|
||||
|
||||
Commands are actions triggered to operate on buffers primarily. They are
|
||||
present in `editor`, `tui`, `mode` and `minimodes`, it's possible to
|
||||
find commands in other places, which will become evident when the need
|
||||
arises.
|
||||
|
||||
[]($section.id('notes'))
|
||||
## Previous notes
|
||||
|
||||
Note: Flow is programmed with [zig](https://ziglang.org/), if you are
|
||||
familiar with C, C++, Rust, there are differences and reasonings that
|
||||
might find useful when [learning Zig](https://ziglang.org/learn/). If
|
||||
you are coming from higher level programming languages such as Python,
|
||||
Ruby, C#, Java, Golang, Typescript it will be an opportunity to learn
|
||||
about trades of managing memory and fast responses and some lower level
|
||||
concepts present in Zig. If you are brand new to programming, some
|
||||
general concepts will be needed and practice in another language before
|
||||
getting into flow development.
|
||||
|
||||
If you are new to Zig, it's a good idea to take a look at
|
||||
[ziglings](https://ziglings.org/) to practice, as you learn the
|
||||
language.
|
||||
|
||||
Maybe there is a [shell command invoked](/docs/architecture/keybind#shell)
|
||||
with a keybinding that can help in the task you are aiming at before
|
||||
developing flow itself.
|
||||
|
||||
[]($section.id('creating'))
|
||||
## Understanding and creating commands
|
||||
|
||||
A command is a function with a type like
|
||||
|
||||
```zig
|
||||
pub fn copy(self: *Self, _: Context) Result
|
||||
```
|
||||
|
||||
and a `Meta` definition with the same name and suffix `_meta`.
|
||||
|
||||
```zig
|
||||
pub const copy_meta: Meta = .{ .description = "Copy selection to clipboard" };
|
||||
```
|
||||
|
||||
`copy` command is defined in `editor.zig`, which copies the current
|
||||
selections into the pimp internal clipboard. Commands are available to
|
||||
all the modes if defined as `pub`.
|
||||
|
||||
`meta` holds the description appearing in the command palette and
|
||||
optionally has arguments, the most common, an integer, that usually
|
||||
constitutes a repetition parameter, targeting vim, emacs and helix
|
||||
modes. As you dig in, there might be particularities on the parameters
|
||||
accepted for a given command.
|
||||
|
||||
[]($section.id('calling'))
|
||||
## Invoking another command
|
||||
|
||||
Commands can be bound to mnemonics in modes by convention. For example,
|
||||
in Vim Mode `vim.zig`, `q` corresponds to (quit), the most famous one.
|
||||
|
||||
```zig
|
||||
pub fn q(_: *void, _: Ctx) Result {
|
||||
try command.cmd("quit", .{});
|
||||
}
|
||||
pub const q_meta: Meta = .{ .description = "q (quit)" };
|
||||
```
|
||||
|
||||
Looking more closely, the first parameter in this case is of `*void`
|
||||
type, given that this command is defined in `vim.zig` which is calling
|
||||
the `quit` command defined in `editor.zig`. `cmd` takes care of routing
|
||||
and finding the command wherever it is defined.
|
||||
|
||||
[]($section.id('tldr'))
|
||||
## Chaining commands
|
||||
|
||||
Chaining commands is also common, and, by the way, swift. This is a
|
||||
sample of applying first `save_file` command and then, the command
|
||||
`quit`.
|
||||
|
||||
```zig
|
||||
pub fn wq(_: *void, _: Ctx) Result {
|
||||
try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } }));
|
||||
}
|
||||
pub const wq_meta: Meta = .{ .description = "wq (write file and quit)" };
|
||||
```
|
||||
|
||||
`cmd` is in charge of finding a command given its name, and parameters
|
||||
sent to commands vary for each command.
|
||||
|
||||
Sometimes [keybinding](/docs/architecture/keybind) is enough to
|
||||
accomplish a compound of already present commands.
|
||||
|
||||
[]($section.id('deepen'))
|
||||
## Code organization
|
||||
|
||||
Is common to define private functions in a given module that are
|
||||
invoked from commands, as usual, functions are meant to be reused and
|
||||
help organize code.
|
||||
|
||||
For example, in hx mode `helix.zig` the `select_to_char_left_helix`
|
||||
command uses the functions `helix_with_selections_const_arg` which
|
||||
iterates over all cursels and applies the
|
||||
`select_cursel_to_char_left_helix` function.
|
||||
|
||||
```zig
|
||||
pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result {
|
||||
try helix_with_selections_const_arg(ctx, &select_cursel_to_char_left_helix);
|
||||
}
|
||||
```
|
||||
|
||||
[]($section.id('command_arguments'))
|
||||
### Sending parameters to commands
|
||||
|
||||
`goto_line` (in the case of vim and helix mode, you first type the
|
||||
number and then the action, `gg`) is a command that exemplifies
|
||||
receiving an integer parameter. As stated in its meta:
|
||||
|
||||
```zig
|
||||
pub const goto_line_meta: Meta = .{ .arguments = &.{.integer} };
|
||||
```
|
||||
|
||||
and to actually receiving the integer parameter, `goto_line` will
|
||||
extract from the context like this:
|
||||
|
||||
```zig
|
||||
pub fn goto_line(self: *Self, ctx: Context) Result {
|
||||
|
||||
var line: usize = 0;
|
||||
if (!try ctx.args.match(.{tp.extract(&line)}))
|
||||
return error.InvalidGotoLineArgument;
|
||||
```
|
||||
|
||||
To send a parameter to a command, make sure that the type is exactly
|
||||
the same when retrieving it. We will refer as encode and decode when
|
||||
packing parameters in the context. To pack the `command.fmt` we will
|
||||
encode it like this, when invoking `goto_line`.
|
||||
|
||||
```zig
|
||||
var the_line: usize = 43;
|
||||
try command.cmd("goto_line", command.fmt(.{the_line - 1}));
|
||||
```
|
||||
|
||||
Or calling the command directly, if we have a reference to the object
|
||||
that holds the command.
|
||||
|
||||
```zig
|
||||
var the_line: usize = 43;
|
||||
try ed.goto_line(command.fmt(.{the_line - 1}));
|
||||
```
|
||||
|
||||
It's possible to pass multiple parameters to commands, including arrays
|
||||
and json, packing all of them in Command.Context.
|
||||
|
||||
A deeper explanation of the rules about parameter passing is exposed in
|
||||
[inner data exchange](/docs/architecture/inner_data_exchange), given
|
||||
that parameters can be sent not only to commands, but other broather
|
||||
use cases.
|
||||
|
||||
[]($section.id('next'))
|
||||
## Next steps
|
||||
|
||||
* [minimode](/docs/architecture/minimode) shows argument passing to
|
||||
commands in reaction to keypresses.
|
||||
* [Palettes](/docs/architecture/palette) invoke commands and pass
|
||||
parameters to them.
|
||||
* [Add tests](/docs/testing) to harden your code
|
||||
* [Back to architecture](/docs/architecture)
|
||||
159
content/docs/architecture/editor.smd
Normal file
159
content/docs/architecture/editor.smd
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
.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",
|
||||
},
|
||||
---
|
||||
|
||||
The `editor` coordinates visualization and modification of
|
||||
buffer contents, multiple cursors, selections and marks.
|
||||
|
||||
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 `primary Cursor` is presented always in the `Editor`,
|
||||
signaling the part of the `Buffer` that can be modified and
|
||||
manipulated as you see it. It scrolls on the current visible
|
||||
portion of the buffer.
|
||||
|
||||
Other cursors can be in the `View` or in regions outside the
|
||||
current view, depending on the size of both the buffer and
|
||||
the editor in your device.
|
||||
|
||||
A `Selection` has two cursors that are not visible, they mark
|
||||
the begin and the end of the selection, and CurSels are actually
|
||||
what allow to have the concept of a cursor with a selection. A
|
||||
`Cursel` is composed by a cursor and optionally a Selection.
|
||||
|
||||
Most of editors operations act on the set of CurSels and the
|
||||
Primary Cursor is a particular case of the general case. And
|
||||
as a note, the Primary Cursor is in fact a CurSel.
|
||||
|
||||
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.
|
||||
|
||||
The Editor will be acting on Buffer.Root which is the root of
|
||||
the tree representing the document that is being edited. The API
|
||||
of the 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.
|
||||
|
||||
```zig
|
||||
fn with_cursor_const
|
||||
fn with_cursel_const
|
||||
fn with_cursels_const
|
||||
fn with_selection_const
|
||||
fn with_cursor_const_arg
|
||||
fn with_cursors_const_arg
|
||||
fn with_cursors_const_once
|
||||
fn with_selection_const_arg
|
||||
fn with_cursors_const_repeat
|
||||
fn with_selections_const_arg
|
||||
fn with_cursor_and_view_const
|
||||
fn with_selections_const_once
|
||||
fn with_cursors_and_view_const
|
||||
fn with_selections_const_repeat
|
||||
fn with_selection_and_view_const
|
||||
fn with_selections_and_view_const
|
||||
```
|
||||
|
||||
[]($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.
|
||||
|
||||
[]($section.id("next"))
|
||||
## Next steps
|
||||
|
||||
* [minimodes](/docs/architecture/minimode) invoke commands 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.
|
||||
120
content/docs/architecture/inner_data_exchange.smd
Normal file
120
content/docs/architecture/inner_data_exchange.smd
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
.title = "Command arguments and message passing",
|
||||
.date = @date("2025-10-27T00:00:00"),
|
||||
.author = "CJ van den Berg",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "docs/architecture/inner_data_exchange.smd",
|
||||
.codepath ="src/tui/editor.zig",
|
||||
},
|
||||
---
|
||||
|
||||
Flow uses actor model to offer an snappy experience when working with
|
||||
projects that have tens of thousands of source files, also features
|
||||
async communication with the threads that are working in independent
|
||||
tasks supporting git interface, lsp and tree-sitter integration,
|
||||
apart from the directory introspection to make available all the
|
||||
files of the project, all of them expected s from an IDE. The
|
||||
command arguments travel to the target command and are en/decoded
|
||||
powered by [cbor](https://github.com/neurocyte/cbor), the same as the
|
||||
parameters that are sent from one thread to another. The process
|
||||
management is provided by
|
||||
[thespian](https://github.com/neurocyte/thespian).
|
||||
|
||||
This chapter describes the mechanisms that flow has to pass arguments
|
||||
between components.
|
||||
|
||||
[]($section.id('libraries'))
|
||||
## Library usage
|
||||
|
||||
* The Thespian library sends and receives `thespian.message` values,
|
||||
which are strongly typed, but schema free CBOR encoded structures.
|
||||
It supports spawning, linking, killing, etc., of lightweight processes
|
||||
(aka the "Actor Model" with "Green Threads") and provides async file
|
||||
and network IO and child process management.
|
||||
* The CBOR library encodes decodes CBOR structured data to/from Zig
|
||||
variables
|
||||
* Encoding happens via the `cbor.write*` functions. These are wrapped
|
||||
by `command.fmt` and `thespian.message.fmt` which provide fast
|
||||
allocation free encoding to a thread local buffer. Note that the CBOR
|
||||
data encoded via the `*.fmt` functions will only survive until another
|
||||
message is encoded and must be copied somewhere for more permanent
|
||||
storage, or possibly sent somewhere via thespian.
|
||||
* Decoding happens via the `cbor.match`, `cbor.extract` and
|
||||
`cbor.extractAlloc` group of functions. `cbor.extract` functions do
|
||||
not allocate and cannot be used to extract some types that require allocation.
|
||||
`cbor.extractAlloc` functions _do_ allocate and can extract arrays and
|
||||
structures that require allocation. Both `cbor.extract` and
|
||||
`cbor.extractAlloc` produce strings that **reference** the original CBOR
|
||||
data buffer. `thespian.message.match` and `thespian.extract` functions
|
||||
are fairly simple wrappers.
|
||||
|
||||
The most basic example on deserialization of an integer value is shown
|
||||
in [commands](/docs/architecture/command#command_arguments).
|
||||
|
||||
Cbor features en/decoding arrays, json and compounds of basic types and
|
||||
the only requirement is to decode in the same order as encoding the
|
||||
data, more samples on using cbor can be seen in
|
||||
[cbor tests](https://github.com/neurocyte/cbor/blob/master/test/tests.zig).
|
||||
|
||||
For example, when interacting with the clipboard, the messages sent are
|
||||
multiple chunks of information,
|
||||
|
||||
|
||||
[]($section.id('scoping'))
|
||||
## Buffer scoping
|
||||
|
||||
CBOR structures are mostly stored in a way that avoids allocation
|
||||
entirely. This is really fast, but requires that you always know where
|
||||
the CBOR data you are working with is stored.
|
||||
|
||||
* Received messages are read directly from the thespian process (actor)
|
||||
receive buffer and remain in scope only for the duration of an actor's
|
||||
receive method call
|
||||
* `thespian.message.fmt` encoded messages are stored in the thread local
|
||||
`thespian.message_buffer` and remain in scope only until the next
|
||||
`thespian.message.fmt` call on the same thread
|
||||
* `thespian.exit_message` encoded message are stored in the thread local
|
||||
`thespian.error_message_buffer` and remain in scope only until the next
|
||||
`thespian.exit_message` call on the same thread
|
||||
* `command.fmt` encoded messages are stored in the thread local
|
||||
`command.context_buffer` and remain in scope only until the next
|
||||
`command.fmt` call on the same thread
|
||||
|
||||
All of this implies several things worth keeping in mind:
|
||||
|
||||
* `thespian.pid.send` will encode it's parameters to
|
||||
`thespian.message_buffer` and then send them to the destination actor's
|
||||
receive buffer. This will invalidate the contents of
|
||||
`thespian.message_buffer` and destroy any message previously encoded
|
||||
with `thespian.message.fmt` (on the same thread).
|
||||
* Calling `command.fmt` inside a command that uses
|
||||
`command.Context.args` will possibly invalidate the command's own
|
||||
arguments. I say _possibly_ because the `ctx.arg` may come from
|
||||
somewhere else entirely, like the actor's receive buffer if the command
|
||||
was called remotely, or some other explicitly allocated buffer.
|
||||
* Use `*.fmtbuf` to encode to different buffer if there may be scoping
|
||||
issues. You can allocate and scope this buffer any way you want.
|
||||
* Calling `thespian.exit_message` while propagating an error up the
|
||||
stack that was previously created with `thespian.exit_message` will
|
||||
overwrite the original error
|
||||
* Don't ever try to free a CBOR buffer unless you know exactly where it
|
||||
came from.
|
||||
* Strings extracted from CBOR buffers are **references** into the
|
||||
original CBOR data and will be invalidated implicitly when the CBOR
|
||||
buffer they came from is invalidated/overwritten.
|
||||
|
||||
[]($section.id('next'))
|
||||
## Next steps
|
||||
|
||||
* [Commands](/docs/architecture/command)
|
||||
* [Minimodes](/docs/architecture/minimode)
|
||||
* [Architecture](/docs/architecture)
|
||||
|
||||
[]($section.id('more'))
|
||||
## More information
|
||||
|
||||
* [Deepwiki on cbor](https://deepwiki.com/neurocyte/cbor)
|
||||
* [Samples of cbor usage](https://github.com/neurocyte/cbor/blob/master/test/tests.zig)
|
||||
* [Deepwiki on thespian](https://deepwiki.com/neurocyte/thespian)
|
||||
179
content/docs/architecture/keybind.smd
Normal file
179
content/docs/architecture/keybind.smd
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
---
|
||||
.title = "Keybinding",
|
||||
.date = @date("2025-10-19T00:00:00"),
|
||||
.author = "Igor Támara",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "/docs/architecture/keybind.smd",
|
||||
.codepath ="src/keybind/builtin/",
|
||||
},
|
||||
---
|
||||
|
||||
If you are here, maybe is because you want to make flow behave according
|
||||
to your key presses preferences or possibly you already have edited your
|
||||
own keybinds to suit your use cases and make everything easier, faster
|
||||
and more fluid when in flow.
|
||||
|
||||
First make sure you are aware of the
|
||||
[existence of ctrl+f2 palette](/docs#key_controls) which
|
||||
exposes a list of commands available for you to use, and when you select
|
||||
a command, it's pasted in your current cursor position.
|
||||
|
||||
Using the command palette `Ctrl+Shift+p` and typing **Edit key
|
||||
bindings**, takes you to a json file to extend flow, configuring
|
||||
keybindings to suit your needs. According to the mode you are in, your
|
||||
personal mode's file configuration will be opened. Explore the
|
||||
the file to discover how commands are bound to some combos, key presses
|
||||
and the different [imodes](#hierarchy) present.
|
||||
|
||||
Command palette can also be reached left clicking on the current
|
||||
mode in the status bar.
|
||||
|
||||
[]($section.id('tldr'))
|
||||
## ;TLDR;
|
||||
|
||||
Once you open the corresponding json file, locate inside the
|
||||
[imode](#hierarchy)(internal mode) that will accept the key or
|
||||
key/combos and add an array, where the first element is the combination
|
||||
to map to the commands that will be invoked, the array accepts strings
|
||||
like in
|
||||
|
||||
```js
|
||||
["ctrl+alt+shift+p", "open_command_palette"]
|
||||
```
|
||||
|
||||
To avoid screwing up the combinations, and putting flow in an unusable
|
||||
state derived from a wrong mapping of key combinations, read on.
|
||||
|
||||
[]($section.id('defaults'))
|
||||
## Resetting keys to factory defaults
|
||||
|
||||
User configured keybindings are stored in Flow's configuration directory
|
||||
under `keys/mode.json` where mode can be `flow`, `emacs`, `vim`, `helix`
|
||||
or customized ones. Removing the keys directory or the particular mode
|
||||
file can take you out from a broken state.
|
||||
|
||||
[]($section.id('modes'))
|
||||
## Keybinds for each mode
|
||||
|
||||
Keybinds are edited per mode, and other modes inherit what is defined
|
||||
in your `flow.json` keybindings. Each mode override keybindings of its
|
||||
parent mode. For example, if you are in **emacs** mode you will be
|
||||
redirected to `emacs.json` and it will override the keybindings from
|
||||
flow, and the default ones defined for emacs mode.
|
||||
|
||||
[introducing keybindings](/devlog/2024#2024-12-05T20:55:00) showcases
|
||||
how to get to edit keybindings.
|
||||
|
||||
[]($section.id('hierarchy'))
|
||||
## Keybindings hierarchy
|
||||
|
||||
Some terminology
|
||||
|
||||
* **Mode**: Stored in a json file, like flow mode declared in
|
||||
`flow.json`.
|
||||
* **Imode**: under the json file.
|
||||
* **Major Imode**: `project` or descendant from `project`.
|
||||
* **Minimodes**: To be used momentarily and do not inherit from
|
||||
`project`.
|
||||
|
||||
In general a keybinding json shows this hierarchy:
|
||||
|
||||
```
|
||||
Mode > Imode > press > Key and commands
|
||||
map > map > array > array(array(string,numbers),strings,numbers)
|
||||
```
|
||||
|
||||
`Mode` is the json file that holds a map, where each entry has a map
|
||||
called `press` that is an array of arrays.
|
||||
|
||||
`project` is the main imode in `flow.json` and it can be noticed that
|
||||
`normal` imode `inherits` from `project`, some modes have `release`,
|
||||
usually one will be using only `press` inside `normal` imode or the
|
||||
specific mode if inside `vim`, `helix` or `emacs` modes.
|
||||
|
||||
Looking further, it can be seen that
|
||||
[minimodes](/docs/architecture/minimode) have their own keybinding
|
||||
mappings defined in a particular imode.
|
||||
|
||||
As stated previously, there is a mode hierarchy, the main mode is flow
|
||||
and other modes inherit from it. We remind that also imodes have a
|
||||
hierarchy and it's common for major imodes to be descendants from
|
||||
`project`.
|
||||
|
||||
[]($section.id('adding'))
|
||||
## Adding a Keybinding
|
||||
|
||||
The most basic case to map a keybind to a command was covered in
|
||||
[TLDR](#tldr) which used the combination of three keys pressed
|
||||
simultaneously `ctrl`, `shift` and `p`, all of them where combined with
|
||||
`+` to execute the command `open_command_palette`.
|
||||
|
||||
A common use case is to add a keybinding to invoke an already existing
|
||||
command and chain it to another, making Flow more suited to your own
|
||||
needs.
|
||||
|
||||
[]($section.id('shell'))
|
||||
## Running shell commands
|
||||
|
||||
For example, `f5` by default is used to run `zig build test` outputting
|
||||
its results to a *scratch buffer* called `test`.
|
||||
|
||||
The original definition is:
|
||||
|
||||
```js
|
||||
["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]],
|
||||
```
|
||||
|
||||
Note that:
|
||||
|
||||
The keybind is `f5`, which maps to the keycode generated by pressing
|
||||
the `f5` key.
|
||||
|
||||
`create_scratchbuffer` is invoked receiving the parameter `*test*`
|
||||
which results in creating a scratch buffer if didn't exist. And then
|
||||
executing the command `shell_execute_insert` that receives the
|
||||
paramaters `zig`, `build`, `test`. This latter command is executing
|
||||
a shell command called `zig` with the parameters `build` and `test`;
|
||||
if you don't have zig installed, it will not work, and you might
|
||||
want to remap `f5` to a different shell command.
|
||||
|
||||
```
|
||||
[
|
||||
"f5",
|
||||
[
|
||||
"create_scratch_buffer",
|
||||
"*test*"
|
||||
],
|
||||
[
|
||||
"shell_execute_insert",
|
||||
"zig",
|
||||
"build",
|
||||
"test"
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
Observe [tasks running](/devlog/2025#2025-01-26T22:11:00) and maybe
|
||||
consider using more keybindings or running tasks for your projects.
|
||||
|
||||
[]($section.id('next'))
|
||||
## Next steps
|
||||
|
||||
If you realized that there is a handy combination that others can
|
||||
benefit from or that a mode lacks the combination and it might be
|
||||
included in flow, look at the
|
||||
[contribution guidelines](/docs/contributing) to submit your findings
|
||||
and solutions.
|
||||
|
||||
Probably binding commands is good, but maybe there is a feature in other
|
||||
text editors that you miss and would love to have at your fingertips.
|
||||
Then it's Zig time: [Adding commands](/docs/architecture/command) to
|
||||
flow.
|
||||
|
||||
|
||||
* Making flow even better with [tests](/docs/testing)
|
||||
* [How to contribute](/docs/contributing)
|
||||
* [Get in touch](https://discord.com/invite/4wvteUPphx) to share your
|
||||
combos
|
||||
116
content/docs/architecture/minimode.smd
Normal file
116
content/docs/architecture/minimode.smd
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
---
|
||||
.title = "Minimodes",
|
||||
.date = @date("2025-10-15T00:00:00"),
|
||||
.author = "Igor Támara",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "/docs/architecture/minimode.smd",
|
||||
.codepath ="src/tui/mode/mini/",
|
||||
},
|
||||
---
|
||||
|
||||
Minimodes commitment is to add functionality to the editor, are opened
|
||||
for short periods of time and have their own set of keybindings to
|
||||
execute an specific action, i.e. find something in the current buffer
|
||||
or in project files, open/save a file, and, in modal modes(like vim
|
||||
and helix), as receiving a number as a prefix to repeat an action many
|
||||
times.
|
||||
|
||||
[]($section.id("anatomy"))
|
||||
## Anatomy of minimodes
|
||||
|
||||
To create a minimode it's needed:
|
||||
|
||||
* A Keybinding
|
||||
* An Action mapping
|
||||
* A Minimode definition
|
||||
|
||||
[]($section.id("keybind"))
|
||||
### Keybinding
|
||||
|
||||
When a key or a keystroke(set of keys) are pressed, the associated
|
||||
minimode gets activated and will start to capture the key/strokes
|
||||
until a special keybinding makes it exit, or an specific action exits
|
||||
the minimode. Head to `src/keybind/builtin/flow.json`(flow keybinds)
|
||||
and look for `mini_find`, where you will know which specific actions
|
||||
are triggered by the keybindings of the `find` minimode.
|
||||
|
||||
[]($section.id("mapping"))
|
||||
### Action mapping
|
||||
|
||||
Actions executed by each minimode are stored one per file under
|
||||
`src/tui/mode/mini/`. The command that opens the door to the minimode
|
||||
is linked from `src/tui/tui.zig` which calls the minimodes dynamically
|
||||
when needed.
|
||||
|
||||
Look for `mini` inside `tui.zig` to find out which minimodes are present
|
||||
and where to look, to learn how each minimode does its own task.
|
||||
|
||||
[]($section.id("definition"))
|
||||
### Minimode definition
|
||||
|
||||
Possibly the simplest minimode that does not require defining a
|
||||
particular widget is the `replace` minimode, used in
|
||||
[helix](/docs/mode/helix) and vim mode. To enter the minimode in
|
||||
Helix while in `NOR` or `INS` use the keybind **r**; it consumes
|
||||
another key and replaces the current character under the main cursor
|
||||
with the immediately pressed key after **r**. If there are multiple
|
||||
selections, all the characters are replaced by the one typed after
|
||||
**r**.
|
||||
|
||||
- The minimode needs to expose a `create` function with type
|
||||
|
||||
```zig
|
||||
pub fn create(Allocator,command.Context) !struct { tui.Mode, tui.MiniMode }
|
||||
```
|
||||
Which is in charge of registering the minimode to be able to receive
|
||||
events and will offer the minimode name, the one that appears in the
|
||||
lower status bar while it is active, to let it be known that the
|
||||
minimode is active. This is where all the instatiations are made. Which
|
||||
leads to
|
||||
|
||||
- The `deinit` function whose type is
|
||||
|
||||
```zig
|
||||
pub fn deinit(*Self)
|
||||
```
|
||||
|
||||
- A `receive` function that will route events received casting the
|
||||
type:
|
||||
|
||||
```zig
|
||||
pub fn receive(*Self, tp.pid_ref, tp.message) error{Exit}!bool
|
||||
```
|
||||
|
||||
- A `commands` field that will expose the minimode `Collection` of
|
||||
`Commands`.
|
||||
|
||||
- An special command `mini_mode_insert_code_point` as an element of the
|
||||
commands collection with type:
|
||||
|
||||
```zig
|
||||
pub fn mini_mode_insert_code_point(*Self, Ctx) Result
|
||||
```
|
||||
|
||||
acting as the default handler of the key presses that the minimode will
|
||||
receive when there is no other keybind defined for the minimode.
|
||||
|
||||
All the keys were handled and managed by the default "invisible" widget
|
||||
that processes the keys for the minimode. And there is room for custom
|
||||
widgets.
|
||||
|
||||
[]($section.id("custom_widgets"))
|
||||
## A custom widget
|
||||
|
||||
When there is a need for an specialized widget, it's possible to
|
||||
define one, for example, the `file_browser` is used to load and
|
||||
save files, and `numeric_input` is used to set the tab width for
|
||||
example(look for it in the command palette `:`).
|
||||
|
||||
[]($section.id("next"))
|
||||
## Next steps
|
||||
|
||||
* Head to [architecture](/docs/architecture)
|
||||
* Review [commands](/docs/architecture/command)
|
||||
* Review [keybindings](/docs/architecture/keybind)
|
||||
121
content/docs/architecture/palette.smd
Normal file
121
content/docs/architecture/palette.smd
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
.title = "Palettes",
|
||||
.date = @date("2025-10-20T00:00:00"),
|
||||
.author = "Igor Támara",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "docs/architecture/palette.smd",
|
||||
.codepath ="src/tui/mode/overlay/clipboard_palette.zig",
|
||||
},
|
||||
---
|
||||
|
||||
Palettes are overlay menus with auto complete that allow to select an
|
||||
item from the presented list, applying a command with the selected
|
||||
element, optionally deleting the selected item; it's possible to
|
||||
close the palette without selecting anything(a.k.a. cancel), filter
|
||||
the elements, and having special elements that trigger different
|
||||
actions, for example, the task palette.
|
||||
|
||||
Examples of palettes are `command_palette`, `clipboard_palette`, they
|
||||
all are based on `src/tui/mode/overlay/palette.zig that does all the
|
||||
heavy lifting and sets the framework to create new palettes as simple
|
||||
as possible.
|
||||
|
||||
Palettes are an special case of [minimode] and for instance a mode, they
|
||||
receive inputs from the keyboard and execute the beforehand mentioned
|
||||
actions in response.
|
||||
|
||||
To get the most of this section, it's recommended to have read about
|
||||
[commands](/docs/architecture/command), and optionally,
|
||||
[minimodes](/docs/architecture/minimode).
|
||||
|
||||
[]($section.id("anatomy"))
|
||||
## Defining the palette
|
||||
|
||||
Palettes are under `tui/overlay` and use the facilities offered by
|
||||
`palette.zig` to perform all the operations.
|
||||
|
||||
1. Defining the list of elements
|
||||
2. Filtering the elements
|
||||
3. Perform an action with the selected element
|
||||
|
||||
Note: Palettes are meant to show options and allowing to select the
|
||||
options to execute an action on the given selection. To maintain as
|
||||
readable as possible the code and thus extensible, the data to be used
|
||||
should be prepared previously.
|
||||
|
||||
[]($section.id("basic"))
|
||||
### Fields
|
||||
|
||||
Basic fields that give hints to the user and need to be set are:
|
||||
|
||||
```zig
|
||||
pub const label = "Clipboard history";
|
||||
pub const name = " clipboard";
|
||||
pub const description = "clipboard";
|
||||
pub const icon = " ";
|
||||
```
|
||||
|
||||
[]($section.id("entries"))
|
||||
### Defining the list of elements in the palette
|
||||
|
||||
`load_entries` is in charge of populating the visible entries, each one
|
||||
with an index.
|
||||
|
||||
```zig
|
||||
pub fn load_entries(palette: *Type) !usize
|
||||
```
|
||||
|
||||
The index will identify the action to be taken.
|
||||
|
||||
When populating with each entry, there must be a relation that links the
|
||||
option chosen with the required action, and this happens in
|
||||
`add_menu_entry`.
|
||||
|
||||
```zig
|
||||
pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !void {
|
||||
```
|
||||
|
||||
The common line that will be used when registering the event to a
|
||||
selected item is
|
||||
|
||||
```zig
|
||||
try palette.menu.add_item_with_handler(value, select);
|
||||
```
|
||||
|
||||
Which will apply the `select` function when the value is selected.
|
||||
|
||||
|
||||
[]($section.id("select"))
|
||||
### Acting on selection
|
||||
|
||||
When the selection happens, it is time to invoke the command with the
|
||||
selection making sure to close the palette. Those actions will be
|
||||
handled inside `select`, whose signature is:
|
||||
|
||||
```zig
|
||||
fn select(menu: **Type.MenuType, button: *Type.ButtonType, pos: Type.Pos) void {
|
||||
```
|
||||
|
||||
Other common operations in the palettes can be inspected looking at the
|
||||
source code of the palettes, all of them import `palette.zig`. Once the
|
||||
palette is ready, it's time to make the palette available as a command.
|
||||
|
||||
[]($section.id("register"))
|
||||
## Registering the palette
|
||||
|
||||
Commands that open the palette are defined in `tui.zig` in a similar way
|
||||
it is done with [minimodes](/docs/architecture/minimode). We have got
|
||||
you covered if in doubt about
|
||||
[how to create a command](/docs/architecture/command).
|
||||
|
||||
To view a complete implementation of a palette, take a look at
|
||||
[clipboard history palette commit](https://github.com/neurocyte/flow/commit/634a18cb5685a3c3fcfc08301306e628d33c3256)
|
||||
|
||||
[]($section.id("next"))
|
||||
## Next steps
|
||||
|
||||
* [Minimodes](/docs/architecture/minimode)
|
||||
* [On commands](/docs/architecture/command)
|
||||
* [Architecture](/docs/architecture)
|
||||
|
|
@ -15,7 +15,7 @@ command. The manual is included here for convenience.
|
|||
|
||||
* [Flow Control online help](/docs/help)
|
||||
|
||||
|
||||
[]($section.id("basic_usage"))
|
||||
## Basic Usage
|
||||
```bash
|
||||
flow file.zig:42 # Open at line 42
|
||||
|
|
@ -24,7 +24,7 @@ flow --list-languages # Show all supported languages
|
|||
flow --help # List of command line options
|
||||
```
|
||||
|
||||
|
||||
[]($section.id("key_controls"))
|
||||
## Key Controls
|
||||
| Command | Action |
|
||||
|---------------------------|----------------------|
|
||||
|
|
|
|||
76
content/docs/mode/helix.smd
Normal file
76
content/docs/mode/helix.smd
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
.title = "Helix Mode",
|
||||
.date = @date("2025-10-10T00:00:00"),
|
||||
.author = "Igor Támara",
|
||||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "/docs/mode/helix.smd",
|
||||
.codepath = "src/tui/mode/helix.zig",
|
||||
},
|
||||
---
|
||||
|
||||
This document describes implementation of Helix Mode.
|
||||
|
||||
[]($section.id('what'))
|
||||
## What and what not
|
||||
|
||||
The first and biggest difference is that Flow has a mode that emulates
|
||||
Helix, or at least has equivalent of the worthiest actions that can be
|
||||
done with Helix. The conversely is not true.
|
||||
|
||||
`:` opens up Flow's rich command palette that might have
|
||||
functionalities Helix users are used to have, if you find something
|
||||
missing, it's possible to
|
||||
[open a Feature Request](https://github.com/neurocyte/flow/issues),
|
||||
make sure to review
|
||||
[other issues](https://github.com/neurocyte/flow/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelix-mode)
|
||||
to avoid repeating or see if there is anyone interested in porting on
|
||||
[Discord](https://discord.gg/kzJC9fA7) to ask if or there is a
|
||||
workaround, remember that it's possible to bounce back to Flow mode
|
||||
if needed.
|
||||
|
||||
[]($section.id('enhancing'))
|
||||
## Enhancing hx mode
|
||||
|
||||
This is a programmer's editor, you are more than welcome to enhance to
|
||||
suit your needs that maybe coincide with others.
|
||||
|
||||
Please take a look at [architecture](/docs/architecture) and
|
||||
[contributing](/docs/contributing) for an overview and the mechanics
|
||||
of getting your changes into Flow.
|
||||
|
||||
hx mode is modal kind, the same as vim mode, and the file that has the
|
||||
particular work to make it real is in `src/tui/mode/helix.zig`, adding
|
||||
a `command` and the corresponding `meta` is what is required.
|
||||
[More on commands](/docs/architecture/command).
|
||||
|
||||
[]($section.id('pickers'))
|
||||
### Pickers
|
||||
|
||||
Flow hx mode offers most of the picker types equivalents with `panels`
|
||||
and [palettes](/docs/architecture/palette). Example of panels are
|
||||
the `g` `r` (go to reference from lsp) and `space` `/` (a.k.a find in
|
||||
files). Examples of `palettes` are `space` `b` to pick a buffer or
|
||||
`space` `f` to open a file in your project. Panels open below the
|
||||
editor while palettes open overlapping the working area.
|
||||
|
||||
One medium sized project is to create a widget that has one input
|
||||
widget with two panels, on the left, the list of options and, on the
|
||||
right, the preview of the selected option and offer various keybindings
|
||||
to manipulate the objects inside both panels with filtering.
|
||||
|
||||
[]($section.id('next'))
|
||||
## Next steps
|
||||
|
||||
Said all of this, it's possible to start contributing via pull
|
||||
requesting [keybinds](/docs/architecture/keybind),
|
||||
[commands](/docs/architecture/command),
|
||||
[palettes](/docs/architecture/palette), or the special widget
|
||||
mentioned previously.
|
||||
|
||||
More about the [architecture](/docs/architecture) or jump to
|
||||
[contribution guidelines](/docs/contributing).
|
||||
|
||||
Join the [#helix-mode channel](https://discord.gg/sxdejrAA) and get in
|
||||
touch with other hx users.
|
||||
|
|
@ -5,12 +5,13 @@
|
|||
.layout = "tutorial.shtml",
|
||||
.draft = false,
|
||||
.custom = {
|
||||
.githubedit = "/docs/testing.md",
|
||||
.githubedit = "/docs/testing.smd",
|
||||
.codepath ="test",
|
||||
},
|
||||
---
|
||||
Currently flow tests are aimed to work as unit tests, it always is a good
|
||||
idea to review the
|
||||
Currently flow tests are aimed to work as unit tests.
|
||||
|
||||
If new to zig, it always is a good idea to review the
|
||||
[zig tests documentation](https://ziglang.org/documentation/master/#Zig-Test)
|
||||
and also an
|
||||
[introduction to testing](https://pedropark99.github.io/zig-book/Chapters/03-unittests.html).
|
||||
|
|
@ -26,7 +27,7 @@ Flow tests are placed in the directory `test`.
|
|||
[]($section.id("running_tests"))
|
||||
## Running the tests
|
||||
|
||||
To run the full set of tests, inside flow, use `F5`, which runs a task that
|
||||
To run the full set of tests, inside flow, use `f5`, which runs a task that
|
||||
invokes:
|
||||
|
||||
```
|
||||
|
|
@ -56,8 +57,8 @@ nature, for example, when there are a lot of branching
|
|||
* You find something that could be changed in the future affecting the
|
||||
current behavior
|
||||
* A bug is fixed
|
||||
* A defined behavior could be thought different, for example when in a mode,
|
||||
it was defined that something might diverge from other programs.
|
||||
* A defined behavior could be thought different, for example when in a
|
||||
mode, it was defined that something might diverge from other programs.
|
||||
|
||||
Tests are placed under `test` directory. Add your test in the file that
|
||||
exercises the functionality and makes proof of it behaving as expected.
|
||||
|
|
@ -77,8 +78,8 @@ In such cases the module that has the logic should provide a pub
|
|||
`test_internal`, by convention, exposing the target functionalities to
|
||||
be tested.
|
||||
|
||||
For example in `src/tui/mode/helix.zig`, `test_internal` exposes the private
|
||||
function
|
||||
For example in `src/tui/mode/helix.zig`, `test_internal` exposes the
|
||||
private function
|
||||
|
||||
```zig
|
||||
fn move_cursor_long_word_right_end(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void
|
||||
|
|
@ -156,7 +157,8 @@ of adding a new test file for project manager.
|
|||
## FAQ on tests
|
||||
|
||||
[]($section.id("import_editor"))
|
||||
### I need to test something that requires importing the editor ¿What do I do?
|
||||
### I need to test something that requires importing the editor ¿What
|
||||
do I do?
|
||||
|
||||
There are two paths from here:
|
||||
|
||||
|
|
@ -171,7 +173,26 @@ Refactor the functions involved in the functionality to make them
|
|||
not rely directly with editor and other higher level components, and
|
||||
test the lower level ones.
|
||||
|
||||
An example of this can be seen in commands
|
||||
For example, in `vim NORMAL` mode, the key `F` looks for a character to
|
||||
the left in the same line, if the character is not found it goes to the
|
||||
beginning of the line. In the case of `hx NOR` mode, the `F` key looks
|
||||
for a character to the beginning of the file, if found, makes a
|
||||
selection from the initial cursor position to the character found, if
|
||||
not, no selection is made and the cursor is not moved at all.
|
||||
|
||||
|
||||
Given that Helix has that movement and selection functionality, finding
|
||||
the character was the first action and hence the search function is
|
||||
the one tested in `test/tests_helix.zig`, given that positioning the
|
||||
selection is rather simple compared to looking for the character. It
|
||||
was decided to test the search functionality making it not depend
|
||||
on editor, but only on the cursor, buffer, metrics and context, all
|
||||
of them do not require graphic elements at all.
|
||||
|
||||
The group of functions `beyond_eol` can be seen in
|
||||
[this commit](https://github.com/neurocyte/flow/pull/330/commits/baac14b3ae5243cef6461df42dae6fcf5ea15201)
|
||||
and whose tests are
|
||||
[here](https://github.com/neurocyte/flow/pull/330/commits/38a08aed49f4fbba18aab9ccbd3c8b9758414221).
|
||||
|
||||
[]($section.id("end_to_end"))
|
||||
### Use additional tools to test a running flow session
|
||||
|
|
@ -187,5 +208,6 @@ If in doubt about how to do something,
|
|||
## Next steps
|
||||
|
||||
* [How to contribute](/docs/contributing)
|
||||
* [User Documentation](/docs)
|
||||
* [Personalizing keybindings](/docs/architecture/keybind)
|
||||
* [Enhance flow with commands](/docs/architecture/command)
|
||||
* [Other Flow topics](/docs/architecture)
|
||||
Loading…
Add table
Add a link
Reference in a new issue