Compare commits

...

2 Commits

Author SHA1 Message Date
Benedikt Peetz a39a0875a3 fix(src): Adapt the code to compile with trixy
The term "to compile" was specifically chosen, as this code does nothing
more. I would honestly be surprised if it worked in this state.
2024-05-03 21:25:09 +02:00
Benedikt Peetz 6ef6bea61c chore(Cargo): Update to the new external crates
Note that this commit does not contain the needed code changes
2024-05-03 21:23:26 +02:00
18 changed files with 900 additions and 719 deletions

776
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -0,0 +1,142 @@
/*
* 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

@ -1,8 +0,0 @@
%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,150 +1,14 @@
// Use `cargo expand app::command_interface::command_list` for an overview of the file contents
// Run the `api` bin to see the generated api
use language_macros::parse_command_enum;
use crate::app::{events::Event, COMMAND_TRANSMITTER};
// 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;
include!(concat!(env!("OUT_DIR"), "/api.rs"));
parse_command_enum! {
commands {
/// 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>
declare print: fn(CommandTransferValue),
pub fn handle_cmd(cmd: Commands) {
let tx = COMMAND_TRANSMITTER
.get()
.expect("The cell should always be populated, at this point");
namespace 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
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(),
},
},
},
},
}
// FIXME: The None here is definitely wrong <2024-05-03>
tx.send(Event::CommandEvent(cmd, None));
}

View File

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

View File

@ -1,31 +1,36 @@
use std::{collections::HashMap, str::FromStr};
use std::{mem, str::FromStr};
use anyhow::{Error, Result};
use crate::{
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 crossterm::event::Event;
use keymaps::{
key_repr::{Key, Keys},
trie::Node,
};
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,
};
use trixy::oneshot;
pub async fn handle(
app: &mut App<'_>,
command: &Command,
output_callback: Option<oneshot::Sender<CommandTransferValue>>,
command: &Commands,
// FIXME(@soispha): The `String` is temporary <2024-05-03>
output_callback: Option<oneshot::Sender<String>>,
) -> Result<EventStatus> {
// 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).
@ -51,110 +56,90 @@ pub async fn handle(
app.status.add_error_message(format!($str, $($args),+))
};
}
macro_rules! send_main_output {
($str:expr) => {
if let Some(sender) = output_callback {
sender
.send(CommandTransferValue::from($str))
.map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
}
};
($str:expr, $($args:ident),+) => {
if let Some(sender) = output_callback {
sender
.send(CommandTransferValue::from(format!($str, $($args),+)))
.map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
}
};
}
// macro_rules! send_main_output {
// ($str:expr) => {
// if let Some(sender) = output_callback {
// sender
// .send(CommandTransferValue::from($str))
// .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
// }
// };
// ($str:expr, $($args:ident),+) => {
// if let Some(sender) = output_callback {
// sender
// .send(CommandTransferValue::from(format!($str, $($args),+)))
// .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
// }
// };
// }
trace!("Handling command: {:#?}", command);
Ok(match command {
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
}
},
Commands::Trinitrix(trinitrix) => match trinitrix {
Trinitrix::Stdi(_) => {
// No-op I guess
EventStatus::Ok
}
Trinitrix::Api(api) => match api {
Api::Exit => {
Api::exit => {
send_status_output!("Terminating the application..");
EventStatus::Terminate
}
Api::RoomMessageSend(msg) => {
Api::room_message_send { message } => {
if let Some(room) = app.status.room_mut() {
room.send(msg.clone()).await?;
send_status_output!("Sent message: `{}`", msg);
room.send(message.clone()).await?;
send_status_output!("Sent message: `{}`", message);
} else {
// FIXME(@soispha): This should raise an error within lua, as it would
// otherwise be very confusing <2023-09-20>
warn!("Can't send message: `{}`, as there is no open room!", &msg);
warn!(
"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
}
Api::Help(_) => todo!(),
Api::Ui(ui) => match ui {
Ui::CommandLineShow => {
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 => {
Ui::set_mode { mode } => match mode {
Mode::Normal => {
app.status.set_state(State::Normal);
send_status_output!("Set input mode to Normal");
EventStatus::Ok
}
Mode::Insert => {
app.status.set_state(State::Insert);
app.ui.set_input_position(InputPosition::MessageCompose);
send_status_output!("Set input mode to Insert");
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::CyclePlanesRev => {
Ui::cycle_planes_rev => {
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);
send_status_output!("Set input mode to Normal");
EventStatus::Ok
}
Ui::SetModeInsert => {
app.status.set_state(State::Insert);
app.ui.set_input_position(InputPosition::MessageCompose);
send_status_output!("Set input mode to Insert");
EventStatus::Ok
}
},
Api::Keymaps(keymaps) => match keymaps {
Keymaps::Add((mode, key, callback)) => {
Keymaps::add {
mode,
key,
callback,
} => {
mode.chars().for_each(|char| {
info!("Setting keymaping ('{}') for mode '{}'", key, char);
let parsed_keys = key
@ -179,7 +164,7 @@ pub async fn handle(
}
trie.insert(&parsed_keys, callback.to_owned())
.map_err(|err| {
send_error_output!(err.to_string());
send_error_output!(format!("{:#?}", err));
})
.expect("We already dealt with the error")
}
@ -187,60 +172,140 @@ pub async fn handle(
};
});
mem::forget(key);
mem::forget(mode);
EventStatus::Ok
}
// TODO(@soispha): Well.., we should probably add these functions: <2023-10-15>
Keymaps::Remove((mode, key)) => todo!(),
Keymaps::Get(mode) => todo!(),
// FIXME(@soispha): It would be nice to have these functions, but well..
// someone needs to write them <2024-05-03>
Keymaps::remove { mode, key } => todo!(),
Keymaps::get { mode } => todo!(),
},
Api::Raw(raw) => match raw {
Raw::RaiseError(err) => {
send_error_output!(err);
Raw::raise_error { error_message } => {
send_error_output!(error_message);
mem::forget(error_message);
EventStatus::Ok
}
Raw::DisplayOutput(output) => {
Raw::display_output { output_message } => {
// 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
// better, should it?
send_status_output!(output);
send_status_output!(output_message);
mem::forget(output_message);
EventStatus::Ok
}
Raw::Private(_) => {
// no-op, read the comment about it in the `command_list`
Raw::send_input_unprocessed { input } => {
let output = match app.status.state() {
State::Insert => {
let key = Key::from_str(&input)?;
let cross_input: Event = key.try_into()?;
app.ui
.message_compose
.input(tui_textarea::Input::from(cross_input));
EventStatus::Ok
}
State::Command => {
let key = Key::from_str(&input)?;
let cross_input: Event = key.try_into()?;
app.ui
.cli
.as_mut()
.expect("This should exist, when the state is 'Command'")
.input(tui_textarea::Input::from(cross_input));
EventStatus::Ok
}
State::Normal
| State::Setup
| State::KeyInputPending {
old_state: _,
pending_keys: _,
} => EventStatus::Ok,
};
mem::forget(input);
output
}
Raw::Private(private) => {
// no-op, this was used to store functions (not so sure, if we need it
// any longer)
EventStatus::Ok
}
Raw::SendInputUnprocessed(char) => match app.status.state() {
State::Insert => {
let key = Key::from_str(char)?;
let cross_input: Event = key.try_into()?;
app.ui
.message_compose
.input(tui_textarea::Input::from(cross_input));
EventStatus::Ok
}
State::Command => {
let key = Key::from_str(char)?;
let cross_input: Event = key.try_into()?;
app.ui
.cli
.as_mut()
.expect("This should exist, when the state is 'Command'")
.input(tui_textarea::Input::from(cross_input));
EventStatus::Ok
}
State::Normal
| State::Setup
| State::KeyInputPending {
old_state: _,
pending_keys: _,
} => EventStatus::Ok,
},
},
},
Trinitrix::Std(_) => {
// no-op, read the comment about it in the `command_list`
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 crate::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
// 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> {
app.lua.execute_function(function).await;
Ok(EventStatus::Ok)
}
// use anyhow::Result;
//
// use crate::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
// // 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> {
// app.lua.execute_function(function).await;
//
// Ok(EventStatus::Ok)
// }

View File

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

View File

@ -1,14 +1,14 @@
use anyhow::Result;
use cli_log::trace;
use crate::app::{events::EventStatus, App};
// 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
pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> {
trace!("Recieved ci command: `{command}`; executing..");
app.lua.execute_code(command).await;
Ok(EventStatus::Ok)
}
// use anyhow::Result;
// use cli_log::trace;
//
// use crate::app::{events::EventStatus, App};
//
// // 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
// pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> {
// trace!("Recieved ci command: `{command}`; executing..");
//
// app.lua.execute_code(command).await;
//
// Ok(EventStatus::Ok)
// }

View File

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

View File

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

View File

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

4
src/bin/api.rs Normal file
View File

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

View File

@ -22,3 +22,8 @@ async fn main() -> anyhow::Result<()> {
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::*;