Compare commits

..

No commits in common. "a39a0875a366503317532b4953ed360588d4cae6" and "55316f295d5eeaf4b7e0cd3282ee16a4139f858b" have entirely different histories.

18 changed files with 716 additions and 897 deletions

770
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ name = "trinitrix"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
default-run = "trinitrix"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -12,22 +11,16 @@ default = ["tui"]
tui = ["dep:tui", "dep:tui-textarea", "dep:crossterm", "dep:tokio-util", "dep:serde", "dep:indexmap"] tui = ["dep:tui", "dep:tui-textarea", "dep:crossterm", "dep:tokio-util", "dep:serde", "dep:indexmap"]
[dependencies] [dependencies]
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.4.11", features = ["derive"] }
cli-log = "2.0" cli-log = "2.0"
anyhow = "1.0" anyhow = "1.0"
matrix-sdk = "0.6" matrix-sdk = "0.6"
tokio = { version = "1.37", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.35", features = ["macros", "rt-multi-thread"] }
# config
trinitry = {version = "0.1.0"}
keymaps = {version = "0.1.1", features = ["crossterm"] }
# c api
libloading = "0.8.3"
trixy = {version = "0.1.0"}
# lua stuff # lua stuff
mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] } trixy = { path = "./trixy" }
keymaps = { path = "./keymaps", features = ["crossterm"] }
mlua = { version = "0.9.2", features = ["lua54", "async", "send", "serialize"] }
once_cell = "1.19.0" once_cell = "1.19.0"
# tui feature specific parts # tui feature specific parts
@ -36,14 +29,11 @@ tui-textarea = { version = "0.2", features = ["crossterm"], optional = true }
crossterm = { version = "0.25", optional = true } crossterm = { version = "0.25", optional = true }
tokio-util = { version = "0.7", optional = true } tokio-util = { version = "0.7", optional = true }
serde = { version = "1.0", optional = true } serde = { version = "1.0", optional = true }
indexmap = { version = "2.2.6", optional = true } indexmap = { version = "2.1.0", optional = true }
directories = "5.0.1" directories = "5.0.1"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
[build-dependencies]
trixy = { version = "0.1.0" }
[profile.release] [profile.release]
lto = true lto = true

View File

@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1714536327, "lastModified": 1702488130,
"narHash": "sha256-zu4+LcygJwdyFHunTMeDFltBZ9+hoWvR/1A7IEy7ChA=", "narHash": "sha256-Bz4KTuBARAQY8952CpmYVD9o/LoScYjdw8KrK2OjEoA=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "3124551aebd8db15d4560716d4f903bd44c64e4a", "rev": "33dbb6a8342e1cf6252c8976d02ff8a7632aa071",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -41,11 +41,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1710146030, "lastModified": 1701680307,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -56,11 +56,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1714656196, "lastModified": 1702272962,
"narHash": "sha256-kjQkA98lMcsom6Gbhw8SYzmwrSo+2nruiTcTZp5jK7o=", "narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "94035b482d181af0a0f8f77823a790b256b7c3cc", "rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -89,11 +89,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1714702555, "lastModified": 1702520151,
"narHash": "sha256-/NoUbE5S5xpK1FU3nlHhQ/tL126+JcisXdzy3Ng4pDU=", "narHash": "sha256-jxJWosN7hgcW+dFT8V3EBDCYUOjv5tpjEBRmlakS7tU=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "7f0e3ef7b7fbed78e12e5100851175d28af4b7c6", "rev": "d6a1d8f80dbcda4c13993b859a3574c3dde61072",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -68,8 +68,6 @@
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
pkg-config pkg-config
mold-wrapped mold-wrapped
clang-tools
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
openssl openssl

View File

@ -1,142 +0,0 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
//// Prints to the output, with a newline.
// HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded,
// redirecting stdout seems too much like a hack thus we are just redefining the print function
// to output to a controlled output. <2023-09-09>
// This is implemented only for lua
/* fn print(CommandTransferValue); */
mod trinitrix {
/// Language specific functions, which mirror the `trinitrix.api` namespace.
/// That is, if you have to choose between a `std` and a `api` function choose the `std`
/// one as it will most likely be more high-level and easier to use (as it isn't abstracted
/// over multiple languages). Feel free to drop down to the lower level api, if you feel
/// like that more, it should be as stable and user-oriented as the `std` functions
mod stdi {}
/// General API to change stuff in Trinitrix
mod api {
/// Closes the application
fn exit();
/// Send a message to the current room
/// The send message is interpreted literally.
fn room_message_send(message: String);
//// Open the help pages at the first occurrence of
//// the input string if it is Some, otherwise open
//// the help pages at the start
// TODO(@soispha): To be implemented <2024-03-09>
// fn help(Option<String>);
//// Register a function to be used with the Trinitrix api
// (This function is not actually implemented here)
/* declare register_function: false, */
/// Function that change the UI, or UI state
mod ui {
enum Mode {
/// Default mode (navigation mode)
Normal,
/// Allows you to insert things
Insert,
/// actives the command line
Command,
}
/// Change the active mode
fn set_mode(mode: Mode);
/// Go to the next plane
fn cycle_planes();
/// Go to the previous plane
fn cycle_planes_rev();
}
/// Manipulate keymappings, the mode is specified as a String build up of all mode
/// the keymapping should be active in. The mapping works as follows:
/// n => normal Mode
/// c => command Mode
/// i => insert Mode
///
/// The key works in a similar matter, specifying the required keypresses to trigger the
/// callback. For example "aba" for require the user to press "a" then "b" then "a" again
/// to trigger the mapping. Special characters are encoded as follows:
/// "<C-a>ba" => "Ctrl+a" then "b" then "a"
/// "<S-a>" => "A" or "Shift+a"
/// "A" => "A"
/// "<M-a> " => "Alt+a" (<A-a>) or "Meta+a"(<M-a>) (most terminals can't really differentiate between these characters)
/// "a<C-b><C-a>" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super)
/// "<CSM-b>" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter)
/// "a " => "a" then a literal space (" ")
/// "å🙂" => "å" then "🙂" (full Unicode support!)
/// "<ESC>" => escape key
/// "<F3>" => F3 key
/// "<BACKSPACE>" => backspace key (and so forth)
/// "<DASH>" => a literal "-"
/// "<ANGULAR_BRACKET_OPEN>" or "<ABO>" => a literal "<"
/// "<ANGULAR_BRACKET_CLOSE>" or "<ABC>" => a literal ">"
///
/// The callback MUST be registered first by calling
/// `trinitrix.api.register_function()` the returned value can than be used to
/// set the keymap.
mod keymaps {
/// Add a new keymapping
fn add(mode: String, key: String, callback: fn());
/// Remove a keymapping
///
/// Does nothing, if the keymapping doesn't exists yet
fn remove(mode: String, key: String);
/// List declared keymappings
fn get(mode: String);
}
/// Functions only used internally within Trinitrix
mod raw {
/// Send an error to the default error output
fn raise_error(error_message: String);
/// Send output to the default output
/// This is mainly used to display the final
/// output of evaluated lua commands.
fn display_output(output_message: String);
/// Input a character without checking for possible keymaps
/// If the current state does not expect input, this character is ignored
/// The encoding is the same as in the `trinitrix.api.keymaps` commands
fn send_input_unprocessed(input: String);
/// This namespace is used to store some command specific data (like functions, as
/// ensuring memory locations stay allocated in garbage collected language is hard)
///
/// Treat it as an implementation detail
mod __private {}
}
}
}
// Trixy is sort of a subset of rust
// vim: syntax=rust

View File

@ -0,0 +1,8 @@
%s/::core::fmt::Formatter::debug_tuple_field1_finish(f,\(.*\))/println!(\1); return Ok(())
%s/::alloc::fmt::format(format_args!(\n\s*\(".*"\),\n\s*\(".*"\),\n\s*));/format!(\1,\2);
%s/::alloc::fmt::format(format_args!(\n\s*\(".*"\),\n\s*\(".*"\)\n\s*));/format!(\1,\2);
%s/::alloc::fmt::format(format_args!(\(".*"\), \(".*"\)));/format!(\1,\2);
%s/let lvl = ::log::Level::Info;\n\s*if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {\(\n\s*.*\)\{12}//g
%s/::log::__private_api::Option::None,\n\s*);\n\s*}/

View File

@ -1,14 +1,150 @@
// Run the `api` bin to see the generated api // Use `cargo expand app::command_interface::command_list` for an overview of the file contents
use crate::app::{events::Event, COMMAND_TRANSMITTER}; use language_macros::parse_command_enum;
include!(concat!(env!("OUT_DIR"), "/api.rs")); // TODO(@soispha): Should these paths be moved to the proc macro?
// As they are not static, it could be easier for other people,
// if they stay here.
use crate::app::command_interface::command_transfer_value::{
support_types::Function, CommandTransferValue,
};
use crate::app::Event;
use mlua::IntoLua;
pub fn handle_cmd(cmd: Commands) { parse_command_enum! {
let tx = COMMAND_TRANSMITTER commands {
.get() /// Prints to the output, with a newline.
.expect("The cell should always be populated, at this point"); // HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded,
// redirecting stdout seems too much like a hack thus we are just redefining the print function
// to output to a controlled output. <2023-09-09>
declare print: fn(CommandTransferValue),
// FIXME: The None here is definitely wrong <2024-05-03> namespace trinitrix {
tx.send(Event::CommandEvent(cmd, None)); /// Language specific functions, which mirror the `trinitrix.api` namespace.
/// That is, if you have to choose between a `std` and a `api` function choose the `std`
/// one as it will most likely be more high-level and easier to use (as it isn't abstracted
/// over multiple languages). Feel free to drop down to the lower level api, if you feel
/// like that more, it should be as stable and user-oriented as the `std` functions
namespace std {
/// This command is a no-op, it's just here to ensure that the 'std'
/// namespace get actually created
// FIXME(@soispha): Add an attribute to namespaces to avoid having to use
// empty functions <2023-10-14>
declare private_initializer_std: fn(),
},
/// Debug only functions, these are effectively useless
namespace debug {
/// Greets the user
declare greet: fn(String) -> String,
/// Returns a table of greeted users
declare greet_multiple: fn() -> Table,
},
/// General API to change stuff in Name
namespace api {
/// Closes the application
declare exit: fn(),
/// Send a message to the current room
/// The send message is interpreted literally.
declare room_message_send: fn(String),
/// Open the help pages at the first occurrence of
/// the input string if it is Some, otherwise open
/// the help pages at the start
declare help: fn(Option<String>),
// Register a function to be used with the Trinitrix api
// (This function is not actually implemented here)
/* declare register_function: false, */
/// Function that change the UI, or UI state
namespace ui {
/// Shows the command line
declare command_line_show: fn(),
/// Hides the command line
declare command_line_hide: fn(),
/// Go to the next plane
declare cycle_planes: fn(),
/// Go to the previous plane
declare cycle_planes_rev: fn(),
/// Sets the current app mode to Normal / navigation mode
declare set_mode_normal: fn(),
/// Sets the current app mode to Insert / editing mode
declare set_mode_insert: fn(),
},
/// Manipulate keymappings, the mode is specified as a String build up of all mode
/// the keymapping should be active in. The mapping works as follows:
/// n => normal Mode
/// c => command Mode
/// i => insert Mode
///
/// The key works in a similar matter, specifying the required keypresses to trigger the
/// callback. For example "aba" for require the user to press "a" then "b" then "a" again
/// to trigger the mapping. Special characters are encoded as follows:
/// "<C-a>ba" => "Ctrl+a" then "b" then "a"
/// "<S-a>" => "A" or "Shift+a"
/// "A" => "A"
/// "<M-a> " => "Alt+a" (<A-a>) or "Meta+a"(<M-a>) (most terminals can't really differentiate between these characters)
/// "a<C-b><C-a>" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super)
/// "<CSM-b>" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter)
/// "a " => "a" then a literal space (" ")
/// "å🙂" => "å" then "🙂" (full Unicode support!)
/// "<ESC>" => escape key
/// "<F3>" => F3 key
/// "<BACKSPACE>" => backspace key (and so forth)
/// "<DASH>" => a literal "-"
/// "<ANGULAR_BRACKET_OPEN>" or "<ABO>" => a literal "<"
/// "<ANGULAR_BRACKET_CLOSE>" or "<ABC>" => a literal ">"
///
/// The callback MUST be registered first by calling
/// `trinitrix.api.register_function()` the returned value can than be used to
/// set the keymap.
namespace keymaps {
/// Add a new keymapping
declare add: fn((/* mode: */ String, /* key: */ String, /* callback: */ Function)),
/// Remove a keymapping
///
/// Does nothing, if the keymapping doesn't exists
declare remove: fn((/* mode: */ String, /* key: */ String)),
/// List declared keymappings
declare get: fn(/* mode: */ String),
},
/// Functions only used internally within Trinitrix
namespace raw {
/// Send an error to the default error output
declare raise_error: fn(String),
/// Send output to the default output
/// This is mainly used to display the final
/// output of evaluated lua commands.
declare display_output: fn(String),
/// Input a character without checking for possible keymaps
/// If the current state does not expect input, this character is ignored
/// The encoding is the same as in the `trinitrix.api.keymaps` commands
declare send_input_unprocessed: fn(String),
/// This namespace is used to store some command specific data (like functions, as
/// ensuring memory locations stay allocated in garbage collected language is hard)
///
/// Treat it as an implementation detail
namespace __private {
/// This command is a no-op, it's just here to ensure that the '__private'
/// namespace get actually created
// FIXME(@soispha): Add an attribute to namespaces to avoid having to use
// empty functions <2023-10-14>
declare private_initializer_private: fn(),
},
},
},
},
}
} }

View File

@ -1,5 +1,5 @@
pub mod command_list; pub mod command_list;
// pub mod command_transfer_value; pub mod command_transfer_value;
// pub mod lua_command_manager; pub mod lua_command_manager;
pub use command_list::*; pub use command_list::*;

View File

@ -1,36 +1,31 @@
use std::{mem, str::FromStr}; use std::{collections::HashMap, str::FromStr};
use crate::{ use anyhow::{Error, Result};
app::{
command_interface::{
trinitrix::{
api::{keymaps::Keymaps, raw::Raw, ui::Ui, Api},
Trinitrix,
},
Commands,
},
events::EventStatus,
status::State,
App,
},
trinitrix::api::ui::Mode,
ui::central::InputPosition,
};
use anyhow::Result;
use cli_log::{info, trace, warn}; use cli_log::{info, trace, warn};
use crossterm::event::Event; use crossterm::event::Event;
use keymaps::{ use keymaps::{
key_repr::{Key, Keys}, key_repr::{Key, Keys},
trie::Node, trie::Node,
}; };
use trixy::oneshot; use tokio::sync::oneshot;
use crate::{
app::{
command_interface::{
command_transfer_value::{CommandTransferValue, Table},
Api, Command, Debug, Keymaps, Raw, Trinitrix, Ui,
},
events::EventStatus,
status::State,
App,
},
ui::central::InputPosition,
};
pub async fn handle( pub async fn handle(
app: &mut App<'_>, app: &mut App<'_>,
command: &Commands, command: &Command,
output_callback: Option<oneshot::Sender<CommandTransferValue>>,
// FIXME(@soispha): The `String` is temporary <2024-05-03>
output_callback: Option<oneshot::Sender<String>>,
) -> Result<EventStatus> { ) -> Result<EventStatus> {
// A command can both return _status output_ (what you would normally print to stderr) // A command can both return _status output_ (what you would normally print to stderr)
// and _main output_ (the output which is normally printed to stdout). // and _main output_ (the output which is normally printed to stdout).
@ -56,90 +51,110 @@ pub async fn handle(
app.status.add_error_message(format!($str, $($args),+)) app.status.add_error_message(format!($str, $($args),+))
}; };
} }
// macro_rules! send_main_output { macro_rules! send_main_output {
// ($str:expr) => { ($str:expr) => {
// if let Some(sender) = output_callback { if let Some(sender) = output_callback {
// sender sender
// .send(CommandTransferValue::from($str)) .send(CommandTransferValue::from($str))
// .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?; .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
// } }
// }; };
// ($str:expr, $($args:ident),+) => { ($str:expr, $($args:ident),+) => {
// if let Some(sender) = output_callback { if let Some(sender) = output_callback {
// sender sender
// .send(CommandTransferValue::from(format!($str, $($args),+))) .send(CommandTransferValue::from(format!($str, $($args),+)))
// .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?; .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
// } }
// }; };
// } }
trace!("Handling command: {:#?}", command); trace!("Handling command: {:#?}", command);
Ok(match command { Ok(match command {
Commands::Trinitrix(trinitrix) => match trinitrix { Command::Print(output) => {
Trinitrix::Stdi(_) => { let output_str: String = output.to_string();
// No-op I guess send_status_output!(output_str);
EventStatus::Ok EventStatus::Ok
} }
Command::Trinitrix(trinitrix) => match trinitrix {
Trinitrix::Debug(debug) => match debug {
Debug::Greet(msg) => {
send_main_output!("Greeting, {}!", msg);
EventStatus::Ok
}
Debug::GreetMultiple => {
let mut table: Table = HashMap::new();
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
table.insert(
"UserName".to_owned(),
CommandTransferValue::String("James".to_owned()),
);
let mut second_table: Table = HashMap::new();
second_table.insert("interface".to_owned(), CommandTransferValue::Integer(3));
second_table.insert("api".to_owned(), CommandTransferValue::Boolean(true));
table.insert(
"Versions".to_owned(),
CommandTransferValue::Table(second_table),
);
send_main_output!(table);
EventStatus::Ok
}
},
Trinitrix::Api(api) => match api { Trinitrix::Api(api) => match api {
Api::exit => { Api::Exit => {
send_status_output!("Terminating the application.."); send_status_output!("Terminating the application..");
EventStatus::Terminate EventStatus::Terminate
} }
Api::room_message_send { message } => { Api::RoomMessageSend(msg) => {
if let Some(room) = app.status.room_mut() { if let Some(room) = app.status.room_mut() {
room.send(message.clone()).await?; room.send(msg.clone()).await?;
send_status_output!("Sent message: `{}`", message); send_status_output!("Sent message: `{}`", msg);
} else { } else {
// FIXME(@soispha): This should raise an error within lua, as it would // FIXME(@soispha): This should raise an error within lua, as it would
// otherwise be very confusing <2023-09-20> // otherwise be very confusing <2023-09-20>
warn!( warn!("Can't send message: `{}`, as there is no open room!", &msg);
"Can't send message: `{}`, as there is no open room!",
&message
);
} }
// NOTE(@soispha): This is temporary, until trixy can do it automatically <2024-05-03>
mem::forget(message);
EventStatus::Ok EventStatus::Ok
} }
Api::Help(_) => todo!(),
Api::Ui(ui) => match ui { Api::Ui(ui) => match ui {
Ui::set_mode { mode } => match mode { Ui::CommandLineShow => {
Mode::Normal => { app.ui.cli_enable();
app.status.set_state(State::Command);
send_status_output!("CLI online");
EventStatus::Ok
}
Ui::CommandLineHide => {
app.ui.cli_disable();
send_status_output!("CLI offline");
EventStatus::Ok
}
Ui::CyclePlanes => {
app.ui.cycle_main_input_position();
send_status_output!("Switched main input position");
EventStatus::Ok
}
Ui::CyclePlanesRev => {
app.ui.cycle_main_input_position_rev();
send_status_output!("Switched main input position; reversed");
EventStatus::Ok
}
Ui::SetModeNormal => {
app.status.set_state(State::Normal); app.status.set_state(State::Normal);
send_status_output!("Set input mode to Normal"); send_status_output!("Set input mode to Normal");
EventStatus::Ok EventStatus::Ok
} }
Mode::Insert => { Ui::SetModeInsert => {
app.status.set_state(State::Insert); app.status.set_state(State::Insert);
app.ui.set_input_position(InputPosition::MessageCompose); app.ui.set_input_position(InputPosition::MessageCompose);
send_status_output!("Set input mode to Insert"); send_status_output!("Set input mode to Insert");
EventStatus::Ok EventStatus::Ok
} }
Mode::Command => {
app.ui.cli_enable();
app.status.set_state(State::Command);
send_status_output!("Set input mode to CLI");
EventStatus::Ok
}
},
Ui::cycle_planes => {
app.ui.cycle_main_input_position();
send_status_output!("Switched main input position");
EventStatus::Ok
}
Ui::cycle_planes_rev => {
app.ui.cycle_main_input_position_rev();
send_status_output!("Switched main input position; reversed");
EventStatus::Ok
}
}, },
Api::Keymaps(keymaps) => match keymaps { Api::Keymaps(keymaps) => match keymaps {
Keymaps::add { Keymaps::Add((mode, key, callback)) => {
mode,
key,
callback,
} => {
mode.chars().for_each(|char| { mode.chars().for_each(|char| {
info!("Setting keymaping ('{}') for mode '{}'", key, char); info!("Setting keymaping ('{}') for mode '{}'", key, char);
let parsed_keys = key let parsed_keys = key
@ -164,7 +179,7 @@ pub async fn handle(
} }
trie.insert(&parsed_keys, callback.to_owned()) trie.insert(&parsed_keys, callback.to_owned())
.map_err(|err| { .map_err(|err| {
send_error_output!(format!("{:#?}", err)); send_error_output!(err.to_string());
}) })
.expect("We already dealt with the error") .expect("We already dealt with the error")
} }
@ -172,33 +187,31 @@ pub async fn handle(
}; };
}); });
mem::forget(key);
mem::forget(mode);
EventStatus::Ok EventStatus::Ok
} }
// FIXME(@soispha): It would be nice to have these functions, but well.. // TODO(@soispha): Well.., we should probably add these functions: <2023-10-15>
// someone needs to write them <2024-05-03> Keymaps::Remove((mode, key)) => todo!(),
Keymaps::remove { mode, key } => todo!(), Keymaps::Get(mode) => todo!(),
Keymaps::get { mode } => todo!(),
}, },
Api::Raw(raw) => match raw { Api::Raw(raw) => match raw {
Raw::raise_error { error_message } => { Raw::RaiseError(err) => {
send_error_output!(error_message); send_error_output!(err);
mem::forget(error_message);
EventStatus::Ok EventStatus::Ok
} }
Raw::display_output { output_message } => { Raw::DisplayOutput(output) => {
// TODO(@Soispha): This is only used to show the Lua command output to the user. // TODO(@Soispha): This is only used to show the Lua command output to the user.
// Lua commands already receive the output. This should probably be communicated // Lua commands already receive the output. This should probably be communicated
// better, should it? // better, should it?
send_status_output!(output_message); send_status_output!(output);
mem::forget(output_message);
EventStatus::Ok EventStatus::Ok
} }
Raw::send_input_unprocessed { input } => { Raw::Private(_) => {
let output = match app.status.state() { // no-op, read the comment about it in the `command_list`
EventStatus::Ok
}
Raw::SendInputUnprocessed(char) => match app.status.state() {
State::Insert => { State::Insert => {
let key = Key::from_str(&input)?; let key = Key::from_str(char)?;
let cross_input: Event = key.try_into()?; let cross_input: Event = key.try_into()?;
app.ui app.ui
.message_compose .message_compose
@ -206,7 +219,7 @@ pub async fn handle(
EventStatus::Ok EventStatus::Ok
} }
State::Command => { State::Command => {
let key = Key::from_str(&input)?; let key = Key::from_str(char)?;
let cross_input: Event = key.try_into()?; let cross_input: Event = key.try_into()?;
app.ui app.ui
.cli .cli
@ -221,91 +234,13 @@ pub async fn handle(
old_state: _, old_state: _,
pending_keys: _, pending_keys: _,
} => EventStatus::Ok, } => EventStatus::Ok,
}; },
mem::forget(input); },
output },
} Trinitrix::Std(_) => {
Raw::Private(private) => { // no-op, read the comment about it in the `command_list`
// no-op, this was used to store functions (not so sure, if we need it
// any longer)
EventStatus::Ok EventStatus::Ok
} }
}, },
},
},
// Command::Print(output) => {
// let output_str: String = output.to_string();
// send_status_output!(output_str);
// EventStatus::Ok
// }
//
// Command::Trinitrix(trinitrix) => match trinitrix {
// Trinitrix::Debug(debug) => match debug {
// Debug::Greet(msg) => {
// send_main_output!("Greeting, {}!", msg);
// EventStatus::Ok
// }
// Debug::GreetMultiple => {
// let mut table: Table = HashMap::new();
// table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
// table.insert(
// "UserName".to_owned(),
// CommandTransferValue::String("James".to_owned()),
// );
//
// let mut second_table: Table = HashMap::new();
// second_table.insert("interface".to_owned(), CommandTransferValue::Integer(3));
// second_table.insert("api".to_owned(), CommandTransferValue::Boolean(true));
// table.insert(
// "Versions".to_owned(),
// CommandTransferValue::Table(second_table),
// );
// send_main_output!(table);
// EventStatus::Ok
// }
// },
// Trinitrix::Api(api) => match api {
// Api::RoomMessageSend(msg) => {
// }
// Api::Help(_) => todo!(),
// Api::Ui(ui) => match ui {
// Ui::CommandLineShow => {
// }
// Ui::CommandLineHide => {
// app.ui.cli_disable();
// send_status_output!("CLI offline");
// EventStatus::Ok
// }
// Ui::CyclePlanes => {
// }
// Ui::CyclePlanesRev => {
// }
// Ui::SetModeNormal => {
// }
// Ui::SetModeInsert => {
// }
// },
// Api::Keymaps(keymaps) => match keymaps {
// Keymaps::Add((mode, key, callback)) => {
// }
// // TODO(@soispha): Well.., we should probably add these functions: <2023-10-15>
// Keymaps::Remove((mode, key)) => todo!(),
// Keymaps::Get(mode) => todo!(),
// },
// Api::Raw(raw) => match raw {
// Raw::RaiseError(err) => {
// }
// Raw::DisplayOutput(output) => {
// }
// Raw::Private(_) => {
// }
// Raw::SendInputUnprocessed(char) =>
// },
// },
// Trinitrix::Std(_) => {
// // no-op, read the comment about it in the `command_list`
// EventStatus::Ok
// }
// },
}) })
} }

View File

@ -1,13 +1,13 @@
// use anyhow::Result; use anyhow::Result;
//
// use crate::app::{ use crate::app::{
// command_interface::command_transfer_value::support_types::Function, events::EventStatus, App, command_interface::command_transfer_value::support_types::Function, events::EventStatus, App,
// }; };
//
// // TODO(@soispha): We just assume for now that all functions originate in lua. This module will in // TODO(@soispha): We just assume for now that all functions originate in lua. This module will in
// // future versions house check for the language the function came from <2023-10-15> // future versions house check for the language the function came from <2023-10-15>
// pub async fn handle(app: &mut App<'_>, function: Function) -> Result<EventStatus> { pub async fn handle(app: &mut App<'_>, function: Function) -> Result<EventStatus> {
// app.lua.execute_function(function).await; app.lua.execute_function(function).await;
//
// Ok(EventStatus::Ok) Ok(EventStatus::Ok)
// } }

View File

@ -4,13 +4,7 @@ use crossterm::event::Event as CrosstermEvent;
use keymaps::key_repr::{Key, Keys}; use keymaps::key_repr::{Key, Keys};
use crate::app::{ use crate::app::{
command_interface::{ command_interface::{Api::Raw, Command, Raw::SendInputUnprocessed, Trinitrix::Api},
trinitrix::{
api::{raw::Raw, Api},
Trinitrix,
},
Commands,
},
events::{Event, EventStatus}, events::{Event, EventStatus},
status::State, status::State,
App, App,
@ -30,11 +24,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
for key in pending_keys { for key in pending_keys {
app.tx app.tx
.send(Event::CommandEvent( .send(Event::CommandEvent(
Commands::Trinitrix(Trinitrix::Api(Api::Raw( Command::Trinitrix(Api(Raw(SendInputUnprocessed(key.to_string_repr())))),
Raw::send_input_unprocessed {
input: key.to_string_repr(),
},
))),
None, None,
)) ))
.await?; .await?;
@ -44,9 +34,9 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
// Just let the input event slip through if no keymap matches // Just let the input event slip through if no keymap matches
app.tx app.tx
.send(Event::CommandEvent( .send(Event::CommandEvent(
Commands::Trinitrix(Trinitrix::Api(Api::Raw(Raw::send_input_unprocessed { Command::Trinitrix(Api(Raw(SendInputUnprocessed(
input: converted_key.to_string_repr(), converted_key.to_string_repr(),
}))), )))),
None, None,
)) ))
.await?; .await?;
@ -98,9 +88,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
let function = possible_key_map let function = possible_key_map
.value() .value()
.expect("This node is terminal and a child, it should have a value"); .expect("This node is terminal and a child, it should have a value");
app.tx.send(Event::Function(*function)).await?;
function();
// app.tx.send(Event::Function(*function)).await?;
app.status.set_state(old_state.to_owned()); app.status.set_state(old_state.to_owned());
} else { } else {
// The choice does not have a value attached to it (might be a waypoint) // The choice does not have a value attached to it (might be a waypoint)

View File

@ -1,14 +1,14 @@
// use anyhow::Result; use anyhow::Result;
// use cli_log::trace; use cli_log::trace;
//
// use crate::app::{events::EventStatus, App}; use crate::app::{events::EventStatus, App};
//
// // This function is here mainly to reserve this spot for further processing of the lua command. // This function is here mainly to reserve this spot for further processing of the lua command.
// // TODO(@Soispha): Move the lua executor thread code from app to this module // TODO(@Soispha): Move the lua executor thread code from app to this module
// pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> { pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> {
// trace!("Recieved ci command: `{command}`; executing.."); trace!("Recieved ci command: `{command}`; executing..");
//
// app.lua.execute_code(command).await; app.lua.execute_code(command).await;
//
// Ok(EventStatus::Ok) Ok(EventStatus::Ok)
// } }

View File

@ -8,4 +8,4 @@ pub mod matrix;
// ci // ci
pub mod command; pub mod command;
pub mod lua_command; pub mod lua_command;
// pub mod function; pub mod function;

View File

@ -3,18 +3,26 @@ pub mod listeners;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crate::app::{command_interface::Commands, status::State, App}; use crate::app::{
command_interface::{
command_transfer_value::{support_types::Function, CommandTransferValue},
Command,
},
status::State,
App,
};
use cli_log::trace; use cli_log::trace;
use crossterm::event::Event as CrosstermEvent; use crossterm::event::Event as CrosstermEvent;
use handlers::{command, input, lua_command, matrix, setup}; use handlers::{command, function, input, lua_command, matrix, setup};
use tokio::sync::oneshot;
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
InputEvent(CrosstermEvent), InputEvent(CrosstermEvent),
MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse), MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse),
// FIXME(@soispha): The `String` is also wrong <2024-05-03> CommandEvent(Command, Option<oneshot::Sender<CommandTransferValue>>),
CommandEvent(Commands, Option<trixy::oneshot::Sender<String>>),
LuaCommand(String), LuaCommand(String),
Function(Function),
} }
impl Event { impl Event {
@ -29,13 +37,13 @@ impl Event {
.await .await
.with_context(|| format!("Failed to handle command event: `{:#?}`", event)), .with_context(|| format!("Failed to handle command event: `{:#?}`", event)),
Event::LuaCommand(lua_code) => Ok(EventStatus::Terminate), Event::LuaCommand(lua_code) => lua_command::handle(app, lua_code.to_owned())
// lua_command::handle(app, lua_code.to_owned()) .await
// .await .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)),
// .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)), Event::Function(function) => function::handle(app, function.to_owned())
// Event::Function(function) => function::handle(app, function.to_owned()) .await
// .await .with_context(|| format!("Failed to handle function: `{}`", function)),
// .with_context(|| format!("Failed to handle function: `{}`", function)),
Event::InputEvent(event) => match app.status.state() { Event::InputEvent(event) => match app.status.state() {
State::Setup => setup::handle(app, &event).await.with_context(|| { State::Setup => setup::handle(app, &event).await.with_context(|| {
format!("Failed to handle input (setup) event: `{:#?}`", event) format!("Failed to handle input (setup) event: `{:#?}`", event)

View File

@ -6,7 +6,6 @@ pub mod status;
use std::{ use std::{
collections::HashMap, collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::OnceLock,
}; };
use anyhow::{Context, Error, Result}; use anyhow::{Context, Error, Result};
@ -14,12 +13,12 @@ use cli_log::{info, warn};
use directories::ProjectDirs; use directories::ProjectDirs;
use keymaps::trie::Node; use keymaps::trie::Node;
use matrix_sdk::Client; use matrix_sdk::Client;
use tokio::sync::mpsc::{self, Sender}; use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
// use self::command_interface::{ use self::command_interface::{
// lua_command_manager::LuaCommandManager, command_transfer_value::support_types::Function, lua_command_manager::LuaCommandManager,
// }; };
use crate::{ use crate::{
accounts::{Account, AccountsManager}, accounts::{Account, AccountsManager},
@ -41,14 +40,13 @@ pub struct App<'runtime> {
input_listener_killer: CancellationToken, input_listener_killer: CancellationToken,
matrix_listener_killer: CancellationToken, matrix_listener_killer: CancellationToken,
// lua: LuaCommandManager, lua: LuaCommandManager,
project_dirs: ProjectDirs, project_dirs: ProjectDirs,
key_mappings: HashMap<State, Node<extern "C" fn()>>, key_mappings: HashMap<State, Node<Function>>,
} }
pub static COMMAND_TRANSMITTER: OnceLock<Sender<Event>> = OnceLock::new();
impl App<'_> { impl App<'_> {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let path: &std::path::Path = Path::new("userdata/accounts.json"); let path: &std::path::Path = Path::new("userdata/accounts.json");
@ -60,11 +58,6 @@ impl App<'_> {
}; };
let (tx, rx) = mpsc::channel(256); let (tx, rx) = mpsc::channel(256);
COMMAND_TRANSMITTER
.set(tx.clone())
.expect("The cell should always be empty at this point");
Ok(Self { Ok(Self {
ui: central::UI::new()?, ui: central::UI::new()?,
accounts_manager: AccountsManager::new(config)?, accounts_manager: AccountsManager::new(config)?,
@ -75,7 +68,7 @@ impl App<'_> {
input_listener_killer: CancellationToken::new(), input_listener_killer: CancellationToken::new(),
matrix_listener_killer: CancellationToken::new(), matrix_listener_killer: CancellationToken::new(),
// lua: LuaCommandManager::new(tx), lua: LuaCommandManager::new(tx),
// TODO: We probably want to populate the strings below a bit more <2023-09-09> // TODO: We probably want to populate the strings below a bit more <2023-09-09>
project_dirs: ProjectDirs::from("", "", "trinitrix").context( project_dirs: ProjectDirs::from("", "", "trinitrix").context(

View File

@ -1,4 +0,0 @@
fn main() {
let output = include_str!(concat!(env!("OUT_DIR"), "/api.rs"));
println!("{}", output);
}

View File

@ -2,22 +2,22 @@ use std::path::PathBuf;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
// TODO: The description is wrong // TODO: The description could be better
/// A terminal client for the matrix chat protocol /// A terminal client for the matrix chat protocol
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
pub struct Args { pub struct Args {
#[command(subcommand)] #[command(subcommand)]
/// The subcommand to execute, default is start /// The subcommand to execute, default is start
pub subcommand: Option<Command>, pub subcommand: Option<Command>,
#[arg(long, short)] #[clap(value_parser, long, short)]
/// Path to the Lua config file, executed instead of the normal one /// Path to the Lua config file, executed instead of the normal one
pub lua_config_file: Option<PathBuf>, pub lua_config_file: Option<PathBuf>,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
pub enum Command { pub enum Command {
#[clap(value_parser)]
/// Starts the main TUI client /// Starts the main TUI client
Start {}, Start {},
} }

View File

@ -22,8 +22,3 @@ async fn main() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
// FIXME(@soispha): Re-exports for trixy, this should be configurable <2024-05-03>
pub use crate::app::command_interface::handle_cmd;
pub use crate::app::command_interface::Commands;
pub use crate::app::command_interface::*;