Add triggers + removing triggers / aliases.

This commit is contained in:
Condorra 2024-09-28 21:39:16 +10:00
parent ecdbb3cfa7
commit a8c279e13e
7 changed files with 229 additions and 23 deletions

36
Cargo.lock generated
View File

@ -1139,6 +1139,15 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "strip-ansi-escapes"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa"
dependencies = [
"vte",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -1284,12 +1293,38 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vte"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [
"utf8parse",
"vte_generate_state_changes",
]
[[package]]
name = "vte_generate_state_changes"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -1396,6 +1431,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_json", "serde_json",
"strip-ansi-escapes",
"thiserror", "thiserror",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",

View File

@ -23,3 +23,4 @@ serde = "1.0.209"
serde_json = "1.0.127" serde_json = "1.0.127"
gc-arena = { git = "https://github.com/kyren/gc-arena.git", rev = "5a7534b883b703f23cfb8c3cfdf033460aa77ea9" } gc-arena = { git = "https://github.com/kyren/gc-arena.git", rev = "5a7534b883b703f23cfb8c3cfdf033460aa77ea9" }
regex = "1.10.6" regex = "1.10.6"
strip-ansi-escapes = "0.2.0"

View File

@ -158,8 +158,16 @@ pub fn install_lua_globals(
) )
.map_err(|_| Error::msg("Can't add command"))?; .map_err(|_| Error::msg("Can't add command"))?;
}; };
($sym: ident, $name: literal) => {
cmd_table
.set(ctx, ctx.intern_static($name.as_bytes()), $sym(ctx))
.map_err(|_| Error::msg("Can't add command"))?;
};
} }
register_stateless_command!(mud_trigger, "act");
register_stateless_command!(mud_trigger, "action");
register_stateless_command!(alias); register_stateless_command!(alias);
register_stateless_command!(unalias);
register_command!(close_mud); register_command!(close_mud);
register_command!(connect_mud); register_command!(connect_mud);
register_stateless_command!(create_match_table); register_stateless_command!(create_match_table);
@ -170,6 +178,10 @@ pub fn install_lua_globals(
register_command!(hsplit); register_command!(hsplit);
register_command!(panel_merge); register_command!(panel_merge);
register_command!(sendmud_raw); register_command!(sendmud_raw);
register_stateless_command!(mud_trigger, "untrigger");
register_stateless_command!(mud_untrigger, "unact");
register_stateless_command!(mud_untrigger, "unaction");
register_stateless_command!(mud_untrigger, "untrigger");
register_command!(vsplit); register_command!(vsplit);
ctx.set_global("commands", cmd_table); ctx.set_global("commands", cmd_table);
let info_table = Table::new(&ctx); let info_table = Table::new(&ctx);

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
echo_to_term_frame, echo_to_term_frame,
id_intern::intern_id, id_intern::intern_id,
match_table::{create_match_table, match_table_add}, match_table::{create_match_table, match_table_add, match_table_remove},
GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame,
}; };
use gc_arena::{Gc, Rootable}; use gc_arena::{Gc, Rootable};
@ -15,7 +15,7 @@ use yew::UseStateSetter;
use super::call_checking_metatable; use super::call_checking_metatable;
pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { pub fn alias(ctx: Context<'_>) -> Callback<'_> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let info: Table = ctx.get_global("info")?; let info: Table = ctx.get_global("info")?;
let cur_frame_id: TermFrame = let cur_frame_id: TermFrame =
@ -25,7 +25,7 @@ pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> {
let aliases: UserData = cur_frame.get(ctx, "aliases")?; let aliases: UserData = cur_frame.get(ctx, "aliases")?;
if stack.is_empty() { if stack.is_empty() {
return list_aliases(cur_frame_id, aliases, ctx); return list_match_tab(cur_frame_id, aliases, ctx);
} }
let alias_match: piccolo::String = piccolo::String::from_value( let alias_match: piccolo::String = piccolo::String::from_value(
@ -61,7 +61,42 @@ pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> {
}) })
} }
fn list_aliases<'gc>( pub fn unalias(ctx: Context<'_>) -> Callback<'_> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let info: Table = ctx.get_global("info")?;
let cur_frame_id: TermFrame =
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
let frames: Table = ctx.get_global("frames")?;
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
let aliases: UserData = cur_frame.get(ctx, "aliases")?;
let alias_match: piccolo::String = piccolo::String::from_value(
ctx,
stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("Missing alias match"))?,
)?;
if !stack.is_empty() {
Err(anyhow::Error::msg(
"Extra arguments to unalias command. Repeating the substitution is not necessary.",
))?;
}
stack.push_back(aliases.into_value(ctx));
stack.push_back(alias_match.into_value(ctx));
let seq = async_sequence(&ctx, |locals, mut seq| {
let remove_func = locals.stash(&ctx, Function::Callback(match_table_remove(ctx)));
async move {
seq.call(&remove_func, 0).await?;
Ok(SequenceReturn::Return)
}
});
Ok(piccolo::CallbackReturn::Sequence(seq))
})
}
pub fn list_match_tab<'gc>(
frame: TermFrame, frame: TermFrame,
aliases: UserData<'gc>, aliases: UserData<'gc>,
ctx: Context<'gc>, ctx: Context<'gc>,

View File

@ -2,7 +2,7 @@ use anyhow::Error;
use gc_arena::{Gc, Rootable}; use gc_arena::{Gc, Rootable};
use piccolo::{ use piccolo::{
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue, self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue,
SequenceReturn, StashedTable, StashedValue, Table, UserData, Value, SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value,
}; };
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
use web_sys::console; use web_sys::console;
@ -11,12 +11,13 @@ use yew::UseStateSetter;
use crate::{ use crate::{
command_handler::execute_queue, command_handler::execute_queue,
id_intern::{intern_id, unintern_id}, id_intern::{intern_id, unintern_id},
match_table::{create_match_table, match_table_add, match_table_remove},
telnet::{parse_telnet_buf, TelnetOutput}, telnet::{parse_telnet_buf, TelnetOutput},
websocket::{connect_websocket, send_message_to_mud, WebSocketId}, websocket::{connect_websocket, send_message_to_mud, WebSocketId},
GlobalLayoutCell, GlobalMemoCell, GlobalLayoutCell, GlobalMemoCell, TermFrame,
}; };
use super::{call_checking_metatable, try_unwrap_frame, LuaState}; use super::{call_checking_metatable, list_match_tab, try_unwrap_frame, LuaState};
fn try_unwrap_socketid<'gc>( fn try_unwrap_socketid<'gc>(
ctx: Context<'gc>, ctx: Context<'gc>,
@ -69,10 +70,9 @@ pub fn handle_websocket_output(
pub fn handle_websocket_output_log_err( pub fn handle_websocket_output_log_err(
socket: &WebSocketId, socket: &WebSocketId,
globals: &GlobalMemoCell, globals: &GlobalMemoCell,
engine: &mut LuaState,
input: &[u8], input: &[u8],
) { ) {
match handle_websocket_output(socket, engine, input) { match handle_websocket_output(socket, &mut globals.lua_engine.borrow_mut(), input) {
Ok(()) => {} Ok(()) => {}
Err(e) => console::log_2( Err(e) => console::log_2(
&JsValue::from_str("An error occurred calling the WebSocket input handler"), &JsValue::from_str("An error occurred calling the WebSocket input handler"),
@ -102,12 +102,8 @@ pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Re
.map_err(|err| format!("{}", err)) .map_err(|err| format!("{}", err))
} }
pub fn handle_websocket_close_log_err( pub fn handle_websocket_close_log_err(socket: &WebSocketId, globals: &GlobalMemoCell) {
socket: &WebSocketId, match handle_websocket_close(socket, &mut globals.lua_engine.borrow_mut()) {
globals: &GlobalMemoCell,
engine: &mut LuaState,
) {
match handle_websocket_close(socket, engine) {
Ok(()) => {} Ok(()) => {}
Err(e) => console::log_2( Err(e) => console::log_2(
&JsValue::from_str("An error occurred calling the WebSocket input handler"), &JsValue::from_str("An error occurred calling the WebSocket input handler"),
@ -388,7 +384,18 @@ pub(super) fn mudoutput_line<'gc>(
)?; )?;
let frameroutes: Table = mud.get(ctx, "frameroutes")?; let frameroutes: Table = mud.get(ctx, "frameroutes")?;
let triggers: UserData = mud.get(ctx, "triggers")?;
let default_frame: Value = frameroutes
.iter()
.map(|(_k, v)| Ok::<Table, Error>(Table::from_value(ctx, v)?))
.next()
.unwrap_or_else(|| Ok(ctx.get_global::<Table>("frames")?.get(ctx, 1_i64)?))?
.get(ctx, "frame")?;
console::log_1(&JsValue::from_str(&format!(
"Line to match: {:?}",
line.as_bytes()
)));
let seq = async_sequence(&ctx, |locals, mut seq| { let seq = async_sequence(&ctx, |locals, mut seq| {
let frameroutes: Vec<StashedTable> = frameroutes let frameroutes: Vec<StashedTable> = frameroutes
.iter() .iter()
@ -399,7 +406,16 @@ pub(super) fn mudoutput_line<'gc>(
}) })
.collect(); .collect();
let line = locals.stash(&ctx, line.into_value(ctx)); let line = locals.stash(&ctx, line.into_value(ctx));
let triggers = locals.stash(&ctx, triggers);
let default_frame = locals.stash(&ctx, default_frame);
async move { async move {
call_checking_metatable::<StashedUserData>(
&mut seq,
triggers,
"try_run_sub",
&[line.clone(), default_frame],
)
.await?;
for frameroute in frameroutes { for frameroute in frameroutes {
call_checking_metatable::<StashedTable>( call_checking_metatable::<StashedTable>(
&mut seq, &mut seq,
@ -494,14 +510,124 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
let curr_frame: Value = ctx let curr_frame: Value = ctx
.get_global::<Table>("info")? .get_global::<Table>("info")?
.get(ctx, ctx.intern_static(b"current_frame"))?; .get(ctx, ctx.intern_static(b"current_frame"))?;
// And call new on the frameroute, for the current frame. // And call new on the frameroute, for the current frame.
let seq = async_sequence(&ctx, |locals, mut seq| { let seq = async_sequence(&ctx, |locals, mut seq| {
let frameroute = locals.stash(&ctx, frameroute); let frameroute = locals.stash(&ctx, frameroute);
let curr_frame = locals.stash(&ctx, curr_frame); let curr_frame = locals.stash(&ctx, curr_frame);
let create_match_tab = locals.stash(&ctx, Function::Callback(create_match_table(ctx)));
let mud = locals.stash(&ctx, mud);
async move { async move {
call_checking_metatable::<StashedTable>(&mut seq, frameroute, "new", &[curr_frame]) call_checking_metatable::<StashedTable>(&mut seq, frameroute, "new", &[curr_frame])
.await?; .await?;
seq.call(&create_match_tab, 0).await?;
seq.try_enter(|ctx, locals, _ex, mut stack| {
let matchtab: Value = stack.consume(ctx)?;
locals.fetch(&mud).set(ctx, "triggers", matchtab)?;
Ok(())
})?;
Ok(SequenceReturn::Return)
}
});
Ok(piccolo::CallbackReturn::Sequence(seq))
})
}
pub(super) fn mud_trigger<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let info: Table = ctx.get_global("info")?;
let cur_frame_id: TermFrame =
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
let frames: Table = ctx.get_global("frames")?;
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?;
if linked_mud.is_nil() {
Err(anyhow::Error::msg(
"No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.",
))?;
}
let linked_mud: Table = Table::from_value(ctx, linked_mud)?;
let triggers: UserData = linked_mud.get(ctx, "triggers")?;
if stack.is_empty() {
return list_match_tab(cur_frame_id, triggers, ctx);
}
let trigger_match: piccolo::String = piccolo::String::from_value(
ctx,
stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("Missing alias match"))?,
)?;
let sub_to: piccolo::String = piccolo::String::from_value(
ctx,
stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("Missing substitution match"))?,
)?;
if !stack.is_empty() {
Err(anyhow::Error::msg(
"Extra arguments to trigger command. Try wrapping the action in {}",
))?;
}
stack.push_back(triggers.into_value(ctx));
stack.push_back(trigger_match.into_value(ctx));
stack.push_back(sub_to.into_value(ctx));
let seq = async_sequence(&ctx, |locals, mut seq| {
let add_func = locals.stash(&ctx, Function::Callback(match_table_add(ctx)));
async move {
seq.call(&add_func, 0).await?;
Ok(SequenceReturn::Return)
}
});
Ok(piccolo::CallbackReturn::Sequence(seq))
})
}
pub(super) fn mud_untrigger(ctx: Context<'_>) -> Callback<'_> {
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let info: Table = ctx.get_global("info")?;
let cur_frame_id: TermFrame =
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
let frames: Table = ctx.get_global("frames")?;
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?;
if linked_mud.is_nil() {
Err(anyhow::Error::msg(
"No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.",
))?;
}
let linked_mud: Table = Table::from_value(ctx, linked_mud)?;
let triggers: UserData = linked_mud.get(ctx, "triggers")?;
let trigger_match: piccolo::String = piccolo::String::from_value(
ctx,
stack
.pop_front()
.ok_or_else(|| anyhow::Error::msg("Missing trigger match"))?,
)?;
if !stack.is_empty() {
Err(anyhow::Error::msg(
"Extra arguments to unact command. Giving the commands to trigger is not necessary.",
))?;
}
stack.push_back(triggers.into_value(ctx));
stack.push_back(trigger_match.into_value(ctx));
let seq = async_sequence(&ctx, |locals, mut seq| {
let remove_func = locals.stash(&ctx, Function::Callback(match_table_remove(ctx)));
async move {
seq.call(&remove_func, 0).await?;
Ok(SequenceReturn::Return) Ok(SequenceReturn::Return)
} }
}); });

View File

@ -35,8 +35,9 @@ impl MatchSubTable {
} }
pub fn try_sub(&self, input: &str) -> Option<Vec<ParsedCommand>> { pub fn try_sub(&self, input: &str) -> Option<Vec<ParsedCommand>> {
for record in self.contents.iter() { let cleaned_input = strip_ansi_escapes::strip_str(input);
if let Some(matched) = record.match_regex.captures(input) { for record in self.contents.iter().rev() {
if let Some(matched) = record.match_regex.captures(&cleaned_input) {
let vec = Some( let vec = Some(
record record
.sub_commands .sub_commands

View File

@ -66,7 +66,6 @@ pub fn connect_websocket(
handle_websocket_output_log_err( handle_websocket_output_log_err(
&data_new_id, &data_new_id,
&data_globals, &data_globals,
&mut data_globals.lua_engine.borrow_mut(),
&Uint8Array::new(&data).to_vec(), &Uint8Array::new(&data).to_vec(),
); );
} else if data.has_type::<JsString>() { } else if data.has_type::<JsString>() {
@ -99,11 +98,7 @@ pub fn connect_websocket(
Some(closed_socket) => { Some(closed_socket) => {
closed_socket.closed = true; closed_socket.closed = true;
closed_socket.retained_closures = None; closed_socket.retained_closures = None;
handle_websocket_close_log_err( handle_websocket_close_log_err(&close_id, &close_globals);
&close_id,
&close_globals,
&mut close_globals.lua_engine.borrow_mut(),
);
} }
}, },
); );