Marked editor as draft

All the docs contained in this group of commits are marked as not draft,
except for editor, which I will be working as I get more experience and
get more precise on the various topics related to the editor and groups
of functions.
This commit is contained in:
Igor Támara 2025-10-30 23:35:53 -05:00 committed by CJ van den Berg
parent e837a66b97
commit 0494574c36
9 changed files with 234 additions and 188 deletions

View file

@ -44,7 +44,7 @@ offers services around the set of projects.
[]($section.id("commands")) []($section.id("commands"))
## Editor commands and modes ## Editor commands and modes
When a buffer is active, it has an [Editor](/docs/architecture/editor) When a buffer is active, it has an Editor
attached to it; an editor might have associated tree-sitter support, attached to it; an editor might have associated tree-sitter support,
given the file type detected, and offers common services that are aimed 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 to be used by `Commands` to manipulate the contents of a buffer at a

View file

@ -11,23 +11,26 @@
--- ---
Commands are actions triggered to operate on buffers primarily. They are Commands are actions triggered to operate on buffers primarily. They are
present in `editor`, `tui`, `mode` and `minimodes`, it's possible to find present in `editor`, `tui`, `mode` and `minimodes`, it's possible to
commands in other places, which will become evident when the need arises. find commands in other places, which will become evident when the need
arises.
[]($section.id('notes')) []($section.id('notes'))
## Previous notes ## Previous notes
Note: Flow is programmed with [zig](https://ziglang.org/), if you are Note: Flow is programmed with [zig](https://ziglang.org/), if you are
familiar with C, C++, Rust, there are differences and reasonings that might familiar with C, C++, Rust, there are differences and reasonings that
find useful when [learning Zig](https://ziglang.org/learn/). If you are might find useful when [learning Zig](https://ziglang.org/learn/). If
coming from higher level programming languages such as Python, Ruby, C#, you are coming from higher level programming languages such as Python,
Java, Golang, Typescript it will be an opportunity to learn about trades of Ruby, C#, Java, Golang, Typescript it will be an opportunity to learn
managing memory and fast responses and some lower level concepts present in about trades of managing memory and fast responses and some lower level
Zig. If you are brand new to programming, some general concepts will be concepts present in Zig. If you are brand new to programming, some
needed and practice in another language before getting into flow development. 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 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. [ziglings](https://ziglings.org/) to practice, as you learn the
language.
Maybe there is a [shell command invoked](/docs/architecture/keybind#shell) 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 with a keybinding that can help in the task you are aiming at before
@ -49,20 +52,20 @@ pub const copy_meta: Meta = .{ .description = "Copy selection to clipboard" };
``` ```
`copy` command is defined in `editor.zig`, which copies the current `copy` command is defined in `editor.zig`, which copies the current
selections into the pimp internal clipboard. Commands are available to all selections into the pimp internal clipboard. Commands are available to
the modes if defined as pub. all the modes if defined as `pub`.
`meta` holds the description appearing in the command palette and optionally `meta` holds the description appearing in the command palette and
has arguments, the most common, an integer, that usually constitutes a optionally has arguments, the most common, an integer, that usually
repetition parameter, targeting vim, emacs and helix modes. As you dig in, constitutes a repetition parameter, targeting vim, emacs and helix
there might be particularities on the parameters accepted for a given modes. As you dig in, there might be particularities on the parameters
command. accepted for a given command.
[]($section.id('calling')) []($section.id('calling'))
## Invoking another command ## Invoking another command
Commands can be bound to mnemonics in modes by convention. For example, in Commands can be bound to mnemonics in modes by convention. For example,
Vim Mode `vim.zig`, `q` corresponds to (quit), the most famous one. in Vim Mode `vim.zig`, `q` corresponds to (quit), the most famous one.
```zig ```zig
pub fn q(_: *void, _: Ctx) Result { pub fn q(_: *void, _: Ctx) Result {
@ -71,16 +74,17 @@ pub fn q(_: *void, _: Ctx) Result {
pub const q_meta: Meta = .{ .description = "q (quit)" }; pub const q_meta: Meta = .{ .description = "q (quit)" };
``` ```
Looking more closely, the first parameter in this case is of `*void` type, Looking more closely, the first parameter in this case is of `*void`
given that this command is defined in `vim.zig` which is calling the `quit` type, given that this command is defined in `vim.zig` which is calling
command defined in `editor.zig`. `cmd` takes care of routing and finding the `quit` command defined in `editor.zig`. `cmd` takes care of routing
the command wherever it is defined. and finding the command wherever it is defined.
[]($section.id('tldr')) []($section.id('tldr'))
## Chaining commands ## Chaining commands
Chaining commands is also common, and, by the way, swift. This is a sample Chaining commands is also common, and, by the way, swift. This is a
of applying first `save_file` command and then, the command `quit`. sample of applying first `save_file` command and then, the command
`quit`.
```zig ```zig
pub fn wq(_: *void, _: Ctx) Result { pub fn wq(_: *void, _: Ctx) Result {
@ -89,20 +93,23 @@ pub fn wq(_: *void, _: Ctx) Result {
pub const wq_meta: Meta = .{ .description = "wq (write file and quit)" }; pub const wq_meta: Meta = .{ .description = "wq (write file and quit)" };
``` ```
Sometimes [keybinding](/docs/architecture/keybind) is enough to accomplish `cmd` is in charge of finding a command given its name, and parameters
a compound of already present commands, in others, zig programming is the sent to commands vary for each command.
path to be taken.
Sometimes [keybinding](/docs/architecture/keybind) is enough to
accomplish a compound of already present commands.
[]($section.id('deepen')) []($section.id('deepen'))
## More in depth commands ## Code organization
Is common to define private functions in a given module that are invoked Is common to define private functions in a given module that are
from commands, as usual, functions are meant to be reused and help organize invoked from commands, as usual, functions are meant to be reused and
code. help organize code.
For example, in hx mode `helix.zig` the `select_to_char_left_helix` command For example, in hx mode `helix.zig` the `select_to_char_left_helix`
uses the functions `helix_with_selections_const_arg` which iterates over all command uses the functions `helix_with_selections_const_arg` which
cursels and applies the `select_cursel_to_char_left_helix` function. iterates over all cursels and applies the
`select_cursel_to_char_left_helix` function.
```zig ```zig
pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result { pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result {
@ -113,18 +120,16 @@ pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result {
[]($section.id('command_arguments')) []($section.id('command_arguments'))
### Sending parameters to commands ### Sending parameters to commands
`cmd` is in charge of finding a command given its name, and parameters sent `goto_line` (in the case of vim and helix mode, you first type the
to commands vary for each command. number and then the action, `gg`) is a command that exemplifies
receiving an integer parameter as stated in its meta:
The command `goto_line`, which receives as parameter an integer(in the case
of vim and helix mode, you first type the number and then the action, gg)
as stated in its meta:
```zig ```zig
pub const goto_line_meta: Meta = .{ .arguments = &.{.integer} }; pub const goto_line_meta: Meta = .{ .arguments = &.{.integer} };
``` ```
and to actually receiving the integer parameter: and to actually receiving the integer parameter, `goto_line` will
extract it like this:
```zig ```zig
pub fn goto_line(self: *Self, ctx: Context) Result { pub fn goto_line(self: *Self, ctx: Context) Result {
@ -134,16 +139,28 @@ pub fn goto_line(self: *Self, ctx: Context) Result {
return error.InvalidGotoLineArgument; 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. Hence
for our terminology to send an integer parameter to a command, we
will encode it using `command.fmt` like in
```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, they all will be packed in Command.Context.
A deeper explanation of the rules about parameter passing is exposed in
[inner data exchange](/docs/architecture/inner_data_exchange).
[]($section.id('next')) []($section.id('next'))
## Next steps ## Next steps
There are more advanced topics related to commands that are covered in * [minimode](/docs/architecture/minimode) shows argument passing to
the [editor](/docs/architecture/editor), that deeps in specific details commands in reaction to keypresses.
of the editor and its interaction with cursors, selections and buffers * [Palettes](/docs/architecture/palette) invoke commands and pass
modifications, among others. parameters to them.
* [Add tests](/docs/testing) to harden your code
Learning about interactions with the buffer and editor is present in
[minimodes](/docs/architecture/minimode).
* [Add tests](/docs/testing) to harden your code
* [Back to architecture](/docs/architecture) * [Back to architecture](/docs/architecture)

View file

@ -3,7 +3,7 @@
.date = @date("2025-10-19T00:00:00"), .date = @date("2025-10-19T00:00:00"),
.author = "Igor Támara", .author = "Igor Támara",
.layout = "tutorial.shtml", .layout = "tutorial.shtml",
.draft = false, .draft = true,
.custom = { .custom = {
.githubedit = "docs/architecture/editor.smd", .githubedit = "docs/architecture/editor.smd",
.codepath ="src/tui/editor.zig", .codepath ="src/tui/editor.zig",

View file

@ -13,13 +13,14 @@
Flow uses actor model to offer an snappy experience when working with Flow uses actor model to offer an snappy experience when working with
projects that have tens of thousands of source files, also features projects that have tens of thousands of source files, also features
async communication with the threads that are working in independent async communication with the threads that are working in independent
tasks that contribute to have vcs, lsp and tree-sitter integration, tasks supporting git interface, lsp and tree-sitter integration,
apart from the directory introspection to make available what is apart from the directory introspection to make available all the
expected from an IDE. The command arguments travel to the target files of the project, all of them expected s from an IDE. The
command and are en/decoded powered by command arguments travel to the target command and are en/decoded
[cbor](https://github.com/neurocyte/cbor), the same as the parameters powered by [cbor](https://github.com/neurocyte/cbor), the same as the
that are sent from one thread to another. The process management is parameters that are sent from one thread to another. The process
provided by [thespian](https://github.com/neurocyte/thespian). management is provided by
[thespian](https://github.com/neurocyte/thespian).
This chapter describes the mechanisms that flow has to pass arguments This chapter describes the mechanisms that flow has to pass arguments
between components. between components.
@ -41,20 +42,20 @@ variables
message is encoded and must be copied somewhere for more permanent message is encoded and must be copied somewhere for more permanent
storage, or possibly sent somewhere via thespian. storage, or possibly sent somewhere via thespian.
* Decoding happens via the `cbor.match`, `cbor.extract` and * Decoding happens via the `cbor.match`, `cbor.extract` and
`cbor.extractAlloc` group of functions. `cbor.extract` functions do not `cbor.extractAlloc` group of functions. `cbor.extract` functions do
allocate and cannot be used to extract some types that require allocation. not allocate and cannot be used to extract some types that require allocation.
`cbor.extractAlloc` functions _do_ allocate and can extract arrays and `cbor.extractAlloc` functions _do_ allocate and can extract arrays and
structures that require allocation. Both `cbor.extract` and structures that require allocation. Both `cbor.extract` and
`cbor.extractAlloc` produce strings that **reference** the original CBOR `cbor.extractAlloc` produce strings that **reference** the original CBOR
data buffer. `thespian.message.match` and `thespian.extract` functions data buffer. `thespian.message.match` and `thespian.extract` functions
are fairly simple wrappers. are fairly simple wrappers.
The most basic example on deserialization of an integer value can is shown The most basic example on deserialization of an integer value is shown
in [commands](/docs/architecture/command#command_arguments). in [commands](/docs/architecture/command#command_arguments).
Cbor features en/decoding arrays, json and compounds of basic types and the Cbor features en/decoding arrays, json and compounds of basic types and
only requirement is to decode in the same order as encoding the data, more the only requirement is to decode in the same order as encoding the
samples on using cbor can be seen in data, more samples on using cbor can be seen in
[cbor tests](https://github.com/neurocyte/cbor/blob/master/test/tests.zig). [cbor tests](https://github.com/neurocyte/cbor/blob/master/test/tests.zig).
For example, when interacting with the clipboard, the messages sent are For example, when interacting with the clipboard, the messages sent are
@ -64,9 +65,9 @@ multiple chunks of information,
[]($section.id('scoping')) []($section.id('scoping'))
## Buffer scoping ## Buffer scoping
CBOR structures are mostly stored in a way that avoids allocation entirely. CBOR structures are mostly stored in a way that avoids allocation
This is really fast, but requires that you always know where the CBOR data entirely. This is really fast, but requires that you always know where
you are working with is stored. the CBOR data you are working with is stored.
* Received messages are read directly from the thespian process (actor) * 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 buffer and remain in scope only for the duration of an actor's
@ -86,30 +87,27 @@ All of this implies several things worth keeping in mind:
* `thespian.pid.send` will encode it's parameters to * `thespian.pid.send` will encode it's parameters to
`thespian.message_buffer` and then send them to the destination actor's `thespian.message_buffer` and then send them to the destination actor's
receive buffer. This will invalidate the contents of receive buffer. This will invalidate the contents of
`thespian.message_buffer` and destroy any message previously encoded with `thespian.message_buffer` and destroy any message previously encoded
`thespian.message.fmt` (on the same thread). with `thespian.message.fmt` (on the same thread).
* Calling `command.fmt` inside a command that uses `command.Context.args` * Calling `command.fmt` inside a command that uses
will possibly invalidate the command's own arguments. I say _possibly_ `command.Context.args` will possibly invalidate the command's own
because the `ctx.arg` may come from somewhere else entirely, like the arguments. I say _possibly_ because the `ctx.arg` may come from
actor's receive buffer if the command was called remotely, or some other somewhere else entirely, like the actor's receive buffer if the command
explicitly allocated buffer. was called remotely, or some other explicitly allocated buffer.
* Use `*.fmtbuf` to encode to different buffer if there may be scoping * Use `*.fmtbuf` to encode to different buffer if there may be scoping
issues. You can allocate and scope this buffer any way you want. issues. You can allocate and scope this buffer any way you want.
* Calling `thespian.exit_message` while propagating an error up the stack * Calling `thespian.exit_message` while propagating an error up the
that was previously created with `thespian.exit_message` will overwrite the stack that was previously created with `thespian.exit_message` will
original error overwrite the original error
* Don't ever try to free a CBOR buffer unless you know exactly where it came * Don't ever try to free a CBOR buffer unless you know exactly where it
from. came from.
* Strings extracted from CBOR buffers are **references** into the original * Strings extracted from CBOR buffers are **references** into the
CBOR data and will be invalidated implicitly when the CBOR buffer they came original CBOR data and will be invalidated implicitly when the CBOR
from is invalidated/overwritten. buffer they came from is invalidated/overwritten.
[]($section.id('next')) []($section.id('next'))
## Next steps ## Next steps
Serialization and deserialization occurs almost everywhere
* [Editor](/docs/architecture/editor)
* [Commands](/docs/architecture/command) * [Commands](/docs/architecture/command)
* [Minimodes](/docs/architecture/minimode) * [Minimodes](/docs/architecture/minimode)
* [Architecture](/docs/architecture) * [Architecture](/docs/architecture)

View file

@ -13,11 +13,11 @@
If you are here, maybe is because you want to make flow behave according 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 to your key presses preferences or possibly you already have edited your
own keybinds to suit your needs and are looking for some advanced own keybinds to suit your needs and are looking for some advanced
topics to cope your use cases and make everything easier and topics to cope your use cases and make everything easier, faster and
fluid when in Flow. fluid when in Flow.
Using the command palette `Ctrl+Shift+p` and typing **Edit key Using the command palette `Ctrl+Shift+p` and typing **Edit key
bindings**, takes you to the json file to extend Flow configuring bindings**, takes you to a json file to extend Flow, configuring
keybindings to suit your needs. According to the mode you are in, the keybindings to suit your needs. According to the mode you are in, the
corresponding file will be opened. The palette can also be reached left corresponding file will be opened. The palette can also be reached left
clicking on the current mode in the status bar. clicking on the current mode in the status bar.
@ -26,9 +26,9 @@ clicking on the current mode in the status bar.
## ;TLDR; ## ;TLDR;
Once you open the corresponding json file, locate inside the imode Once you open the corresponding json file, locate inside the imode
(internal mode) that will accept the key or keys/combo and add an array, (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 where the first element is the combination to map to the commands that
will happen, the array accepts strings like in will be invoked, the array accepts strings like in
```js ```js
["ctrl+alt+shift+p", "open_command_palette"] ["ctrl+alt+shift+p", "open_command_palette"]
@ -52,7 +52,7 @@ Keybinds are edited per mode, and other modes inherit what is defined
in your `flow.json` keybindings. Each mode override keybindings of its in your `flow.json` keybindings. Each mode override keybindings of its
parent mode. For example, if you are in **emacs** mode you will be parent mode. For example, if you are in **emacs** mode you will be
redirected to `emacs.json` and it will override the keybindings from redirected to `emacs.json` and it will override the keybindings from
flow. flow, and the default ones defined for emacs mode.
[introducing keybindings](/devlog/2024#2024-12-05T20:55:00) showcases [introducing keybindings](/devlog/2024#2024-12-05T20:55:00) showcases
how to get to edit keybindings. how to get to edit keybindings.
@ -73,20 +73,20 @@ In general a keybinding json shows this hierarchy:
``` ```
Mode > Imode > press > Key and commands Mode > Imode > press > Key and commands
map > map > array > array(array(string,numbers),strings,numbers) map > map > array > array(array(string,numbers),strings,numbers)
``` ```
`Mode` is the json file that holds a map, where each entry has a map `Mode` is the json file that holds a map, where each entry has a map
called `press` that is an array of arrays. called `press` that is an array of arrays.
`project` is the main imode in `flow.json` and it can be noticed that `project` is the main imode in `flow.json` and it can be noticed that
`normal` imode `inherit`s from `project`, some modes have `release`, `normal` imode `inherits` from `project`, some modes have `release`,
usually one will be using only `press` inside `normal` imode or the usually one will be using only `press` inside `normal` imode or the
specific mode if inside `vim`, `helix` or `emacs` modes. specific mode if inside `vim`, `helix` or `emacs` modes.
Looking further, it can be seen that Looking further, it can be seen that
[minimodes](/docs/architecture/minimode) have their keybinding mappings [minimodes](/docs/architecture/minimode) have their own keybinding
defined in a particular imode. mappings defined in a particular imode.
As stated previously, there is a mode hierarchy, the main mode is flow 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 and other modes inherit from it. We remind that also imodes have a
@ -111,20 +111,24 @@ needs.
For example, `f5` by default is used to run `zig build test` outputting For example, `f5` by default is used to run `zig build test` outputting
its results to a *scratch buffer* called `test`. its results to a *scratch buffer* called `test`.
The original definition is:
```js ```js
["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]], ["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]],
``` ```
Note that: Note that:
The keybind is `f5`, which maps to the `f5` keycode. is invoking The keybind is `f5`, which maps to the keycode generated by pressing
`create_scratchbuffer`, receiving the parameter `*test*` which results the `f5` key.
in creating a scratch buffer if didn't exist. And then executing the
command `shell_execute_insert` that receives the paramaters `zig`, `create_scratchbuffer` is invoked receiving the parameter `*test*`
`build`, `test`. This latter command is executing a shell command which results in creating a scratch buffer if didn't exist. And then
called `zig` with the parameters `build` and `test`; if you don't have executing the command `shell_execute_insert` that receives the
zig installed, it will not work, and you might want to remap `f5` to a paramaters `zig`, `build`, `test`. This latter command is executing
different shell command. 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.
``` ```
[ [
@ -155,8 +159,8 @@ to submit your findings and solution.
Probably binding commands is good, but maybe there is a feature in other Probably binding commands is good, but maybe there is a feature in other
text editors that you miss and would love to have it at your fingertips. text editors that you miss and would love to have it at your fingertips.
Then it's Zig time: Then it's Zig time: [Adding commands](/docs/architecture/command) to
[Adding commands](/docs/architecture/command) to flow. flow.
* Making flow even better with [tests](/docs/testing) * Making flow even better with [tests](/docs/testing)

View file

@ -10,13 +10,12 @@
}, },
--- ---
Minimodes can be used by other modes adding functionality Minimodes commitment is to add functionality to the editor, are opened
to the editor, they have their own set of keybindings and for short periods of time and have their own set of keybindings to
are used momentarily for an specific action, i.e. find execute an specific action, i.e. find something in the current buffer
something in the current buffer or in project files, or in project files, open/save a file, and, in modal modes(like vim
open/save a file, and, in modal modes(like vim and helix). and helix), as receiving a number as a prefix to repeat an action many
An example of the latter is using numeric prefixes to times.
repeat an action many times.
[]($section.id("anatomy")) []($section.id("anatomy"))
## Anatomy of minimodes ## Anatomy of minimodes
@ -30,51 +29,46 @@ To create a minimode it's needed:
[]($section.id("keybind")) []($section.id("keybind"))
### Keybinding ### Keybinding
When a key or a keystroke(set of keys) are pressed, the When a key or a keystroke(set of keys) are pressed, the associated
associated minimode gets activated and will start to minimode gets activated and will start to capture the key/strokes
capture the key/strokes until a special keybinding until a special keybinding makes it exit, or an specific action exits
makes it exit, or an specific action exits the minimode. the minimode. Head to `src/keybind/builtin/flow.json`(flow keybinds)
Head to `src/keybind/builtin/flow.json`(flow keybinds) and look for `mini_find`, where you will know which specific actions
and look for `mini_find`, where you will know which are triggered by the keybindings of the `find` minimode.
specific actions are triggered by the keybindings of the
minimode.
[]($section.id("mapping")) []($section.id("mapping"))
### Action mapping ### Action mapping
Actions executed by each minimode are stored one per Actions executed by each minimode are stored one per file under
file under `src/tui/mode/mini/`. The command that `src/tui/mode/mini/`. The command that opens the door to the minimode
opens opens the door to the minimode is linked from is linked from `src/tui/tui.zig` which calls the minimodes dynamically
`src/tui/tui.zig` which calls the minimodes dynamically
when needed. when needed.
Look for `mini` inside `tui.zig` to find out which minimodes Look for `mini` inside `tui.zig` to find out which minimodes are present
are present and where to look to learn how each minimode and where to look, to learn how each minimode does its own task.
does its own task.
[]($section.id("definition")) []($section.id("definition"))
### Minimode definition ### Minimode definition
Possibly the simplest minimode that does not require Possibly the simplest minimode that does not require defining a
defining a particular widget is the `replace` minimode, particular widget is the `replace` minimode, used in
used in [helix](/docs/mode/helix) and vim mode. To enter the [helix](/docs/mode/helix) and vim mode. To enter the minimode in
minimode in Helix while in `NOR` or `INS` use the keybind Helix while in `NOR` or `INS` use the keybind **r**; it consumes
**r**; it consumes another key and replaces the current another key and replaces the current character under the main cursor
character under the main cursor with the immediately pressed with the immediately pressed key after **r**. If there are multiple
key after **r**. If there are multiple selections, all the selections, all the characters are replaced by the one typed after
characters are replaced by the one typed after **r**. **r**.
- The minimode needs to expose a `create` function with - The minimode needs to expose a `create` function with type
type
```zig ```zig
pub fn create(Allocator,command.Context) !struct { tui.Mode, tui.MiniMode } pub fn create(Allocator,command.Context) !struct { tui.Mode, tui.MiniMode }
``` ```
Which is in charge of registering the minimode to be able Which is in charge of registering the minimode to be able to receive
to receive events and will offer the minimode name, the events and will offer the minimode name, the one that appears in the
one that appears in the lower status bar while it is active, lower status bar while it is active, to let it be known that the
to let it be known that the minimode is active. This is minimode is active. This is where all the instatiations are made. Which
where all the instatiations are made. Which leads to leads to
- The `deinit` function whose type is - The `deinit` function whose type is
@ -82,29 +76,29 @@ where all the instatiations are made. Which leads to
pub fn deinit(*Self) pub fn deinit(*Self)
``` ```
- A `receive` function that will route events received - A `receive` function that will route events received casting the
casting the type: type:
```zig ```zig
pub fn receive(*Self, tp.pid_ref, tp.message) error{Exit}!bool pub fn receive(*Self, tp.pid_ref, tp.message) error{Exit}!bool
``` ```
- A `commands` field that will expose the minimode `Collection` - A `commands` field that will expose the minimode `Collection` of
of `Commands`. `Commands`.
- An special command `mini_mode_insert_code_point` as an element - An special command `mini_mode_insert_code_point` as an element of the
of the commands collection with type: commands collection with type:
```zig ```zig
pub fn mini_mode_insert_code_point(*Self, Ctx) Result pub fn mini_mode_insert_code_point(*Self, Ctx) Result
``` ```
acting as the default handler of the key presses that the minimode acting as the default handler of the key presses that the minimode will
will receive when there is no other keybind defined for the minimode. receive when there is no other keybind defined for the minimode.
All the keys were handled and managed by the default "invisible" All the keys were handled and managed by the default "invisible" widget
widget that processes the keys for the minimode. And there is that processes the keys for the minimode. And there is room for custom
room for custom widgets that are explained next. widgets.
[]($section.id("custom_widgets")) []($section.id("custom_widgets"))
## A custom widget ## A custom widget
@ -119,5 +113,4 @@ example(look for it in the command palette `:`).
* Head to [architecture](/docs/architecture) * Head to [architecture](/docs/architecture)
* Review [commands](/docs/architecture/command) * Review [commands](/docs/architecture/command)
* Deep in the [editor](/docs/architecture/editor)
* Review [keybindings](/docs/architecture/keybind) * Review [keybindings](/docs/architecture/keybind)

View file

@ -10,22 +10,24 @@
}, },
--- ---
Palettes are overlay menus that allow to select an item from the Palettes are overlay menus with auto complete that allow to select an
presented list, applying a command with the selected element, optionally item from the presented list, applying a command with the selected
deleting the selected item; it's possible to close the palette without element, optionally deleting the selected item; it's possible to
selecting anything(a.k.a. cancel), filter the elements, and having close the palette without selecting anything(a.k.a. cancel), filter
special elements that trigger different actions. 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 Examples of palettes are `command_palette`, `clipboard_palette`, they
are based on palette.zig that does all the heavy lifting and sets the all are based on `src/tui/mode/overlay/palette.zig that does all the
framework to create new palettes as simple as possible. 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 Palettes are an special case of [minimode] and for instance a mode, they
receive inputs from keyboards and execute the beforehand mentioned receive inputs from the keyboard and execute the beforehand mentioned
actions in response. actions in response.
To get the most of this section, it's recommended to have read about To get the most of this section, it's recommended to have read about
[commands](/docs/architecture/command), and optionally [commands](/docs/architecture/command), and optionally,
[minimodes](/docs/architecture/minimode). [minimodes](/docs/architecture/minimode).
[]($section.id("anatomy")) []($section.id("anatomy"))
@ -69,8 +71,7 @@ The index will identify the action to be taken.
When populating with each entry, there must be a relation that links the When populating with each entry, there must be a relation that links the
option chosen with the required action, and this happens in option chosen with the required action, and this happens in
`add_menu_entry` used when the user writes in the input to filter out `add_menu_entry`.
options.
```zig ```zig
pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !void { pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !void {
@ -80,14 +81,17 @@ The common line that will be used when registering the event to a
selected item is selected item is
```zig ```zig
try palette.menu.add_item_with_handler(value.written(), select); try palette.menu.add_item_with_handler(value, select);
``` ```
Which will apply the `select` function when the value is selected.
[]($section.id("select")) []($section.id("select"))
### Acting on selection ### Acting on selection
When the selection happens, it is time to invoke the command with the When the selection happens, it is time to invoke the command with the
selection and the palette needs to be closed. Those actions will be selection making sure to close the palette. Those actions will be
handled inside `select`, whose signature is: handled inside `select`, whose signature is:
```zig ```zig
@ -96,7 +100,7 @@ fn select(menu: **Type.MenuType, button: *Type.ButtonType, pos: Type.Pos) void {
Other common operations in the palettes can be inspected looking at the 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 source code of the palettes, all of them import `palette.zig`. Once the
are ready, it's time to make the palette available as a command. palette is ready, it's time to make the palette available as a command.
[]($section.id("register")) []($section.id("register"))
## Registering the palette ## Registering the palette
@ -113,6 +117,5 @@ To view a complete implementation of a palette, take a look at
## Next steps ## Next steps
* [Minimodes](/docs/architecture/minimode) * [Minimodes](/docs/architecture/minimode)
* [Editor topics](/docs/architecture/editor)
* [On commands](/docs/architecture/command) * [On commands](/docs/architecture/command)
* [Architecture](/docs/architecture) * [Architecture](/docs/architecture)

View file

@ -12,6 +12,7 @@
This document describes implementation of Helix Mode. This document describes implementation of Helix Mode.
[]($section.id('what'))
## What and what not ## What and what not
The first and biggest difference is that Flow has a mode that emulates The first and biggest difference is that Flow has a mode that emulates
@ -26,12 +27,13 @@ make sure to review
[other issues](https://github.com/neurocyte/flow/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelix-mode) [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 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 [Discord](https://discord.gg/kzJC9fA7) to ask if or there is a
workaoround, remember that it's possible to bounce back to Flow mode workaround, remember that it's possible to bounce back to Flow mode
if needed. if needed.
[]($section.id('enhancing'))
## Enhancing hx mode ## Enhancing hx mode
This is a programmer editor, you are more than welcome to enhance to This is a programmer's editor, you are more than welcome to enhance to
suit your needs that maybe coincide with others. suit your needs that maybe coincide with others.
Please take a look at [architecture](/docs/architecture) and Please take a look at [architecture](/docs/architecture) and
@ -43,6 +45,7 @@ particular work to make it real is in `src/tui/mode/helix.zig`, adding
a `command` and the corresponding `meta` is what is required. a `command` and the corresponding `meta` is what is required.
[More on commands](/docs/architecture/command). [More on commands](/docs/architecture/command).
[]($section.id('pickers'))
### Pickers ### Pickers
Flow hx mode offers most of the picker types equivalents with `panels` Flow hx mode offers most of the picker types equivalents with `panels`
@ -53,9 +56,12 @@ files). Examples of `palettes` are `space` `b` to pick a buffer or
editor while palettes open overlapping the working area. editor while palettes open overlapping the working area.
One medium sized project is to create a widget that has one input One medium sized project is to create a widget that has one input
widget, two panels, on the left, the list of options and, on the right, widget with two panels, on the left, the list of options and, on the
the preview of the selected option and offer various keybindings to right, the preview of the selected option and offer various keybindings
manipulate the objects inside both panels with filtering. 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 Said all of this, it's possible to start contributing via pull
requesting [keybinds](/docs/architecture/keybind), requesting [keybinds](/docs/architecture/keybind),
@ -65,3 +71,6 @@ mentioned previously.
More about the [architecture](/docs/architecture) or jump to More about the [architecture](/docs/architecture) or jump to
[contribution guidelines](/docs/contributing). [contribution guidelines](/docs/contributing).
Join the [#helix-mode channel](https://discord.gg/sxdejrAA) and get in
touch with other hx users.

View file

@ -9,8 +9,9 @@
.codepath ="test", .codepath ="test",
}, },
--- ---
Currently flow tests are aimed to work as unit tests, it always is a good Currently flow tests are aimed to work as unit tests.
idea to review the
If new to zig, it always is a good idea to review the
[zig tests documentation](https://ziglang.org/documentation/master/#Zig-Test) [zig tests documentation](https://ziglang.org/documentation/master/#Zig-Test)
and also an and also an
[introduction to testing](https://pedropark99.github.io/zig-book/Chapters/03-unittests.html). [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")) []($section.id("running_tests"))
## Running the 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: 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 * You find something that could be changed in the future affecting the
current behavior current behavior
* A bug is fixed * A bug is fixed
* A defined behavior could be thought different, for example when in a mode, * A defined behavior could be thought different, for example when in a
it was defined that something might diverge from other programs. mode, it was defined that something might diverge from other programs.
Tests are placed under `test` directory. Add your test in the file that Tests are placed under `test` directory. Add your test in the file that
exercises the functionality and makes proof of it behaving as expected. 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 `test_internal`, by convention, exposing the target functionalities to
be tested. be tested.
For example in `src/tui/mode/helix.zig`, `test_internal` exposes the private For example in `src/tui/mode/helix.zig`, `test_internal` exposes the
function private function
```zig ```zig
fn move_cursor_long_word_right_end(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void 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 ## FAQ on tests
[]($section.id("import_editor")) []($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: 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 not rely directly with editor and other higher level components, and
test the lower level ones. 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")) []($section.id("end_to_end"))
### Use additional tools to test a running flow session ### Use additional tools to test a running flow session
@ -187,5 +208,6 @@ If in doubt about how to do something,
## Next steps ## Next steps
* [How to contribute](/docs/contributing) * [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) * [Other Flow topics](/docs/architecture)