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.
Note: Flow is programmed with zig, if you are familiar with C, C++, Rust, there are differences and reasonings that might find useful when learning Zig. 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 to practice, as you learn the language.
Maybe there is a shell command invoked with a keybinding that can help in the task you are aiming at before developing flow itself.
A command is a function with a type like
pub fn copy(self: *Self, _: Context) Result
and a Meta definition with the same name and suffix _meta.
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.
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.
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.
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.
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 is enough to accomplish a compound of already present 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:
pub const goto_line_meta: Meta = .{ .arguments = &.{.integer} };
and to actually receiving the integer parameter, goto_line will extract from the context like this:
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.
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.
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, given that parameters can be sent not only to commands, but other broader use cases.
Is common to define private functions in a given module meant to be invoked from commands. As usual, reusing code with functions help organize code.
For example, in hx mode src/tui/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.
pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result {
try helix_with_selections_const_arg(ctx, &select_cursel_to_char_left_helix);
}
Editor has a walkthrough when working with selections, modifying the contents of the editor and deepens in concepts that allow understand how the internal interactions happen when editing a document.
Minimodes pass arguments to the editor, if you wonder how to go beyond the current buffer window, when there are actions like going to a specific line or when searching or replacing a character,this is the place.
Palettes are built to open files, change buffers and also pass parameters to commands. Diving out from the buffer and editor.