block: Port block builtin to Rust

Closes #9612.
This commit is contained in:
Clemens Wasser 2023-02-24 20:21:27 +01:00 committed by Mahmoud Al-Qudsi
parent 330e8a86c7
commit 6f5be9bae4
11 changed files with 194 additions and 153 deletions

View file

@ -100,7 +100,7 @@ endif()
# List of sources for builtin functions.
set(FISH_BUILTIN_SRCS
src/builtin.cpp src/builtins/argparse.cpp
src/builtins/bg.cpp src/builtins/bind.cpp src/builtins/block.cpp
src/builtins/bg.cpp src/builtins/bind.cpp
src/builtins/builtin.cpp src/builtins/cd.cpp src/builtins/command.cpp
src/builtins/commandline.cpp src/builtins/complete.cpp
src/builtins/disown.cpp

View file

@ -0,0 +1,158 @@
// Implementation of the block builtin.
use super::shared::{
builtin_missing_argument, builtin_print_help, io_streams_t, STATUS_CMD_ERROR, STATUS_CMD_OK,
STATUS_INVALID_ARGS,
};
use crate::{
builtins::shared::builtin_unknown_option,
ffi::{parser_t, Repin},
wchar::{wstr, L},
wgetopt::{wgetopter_t, wopt, woption, woption_argument_t},
wutil::wgettext_fmt,
};
use libc::c_int;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Scope {
Unset,
Global,
Local,
}
impl Default for Scope {
fn default() -> Self {
Self::Unset
}
}
#[derive(Debug, Clone, Copy, Default)]
struct Options {
scope: Scope,
erase: bool,
print_help: bool,
}
fn parse_options(
args: &mut [&wstr],
parser: &mut parser_t,
streams: &mut io_streams_t,
) -> Result<(Options, usize), Option<c_int>> {
let cmd = args[0];
const SHORT_OPTS: &wstr = L!(":eghl");
const LONG_OPTS: &[woption] = &[
wopt(L!("erase"), woption_argument_t::no_argument, 'e'),
wopt(L!("local"), woption_argument_t::no_argument, 'l'),
wopt(L!("global"), woption_argument_t::no_argument, 'g'),
wopt(L!("help"), woption_argument_t::no_argument, 'h'),
];
let mut opts = Options::default();
let mut w = wgetopter_t::new(SHORT_OPTS, LONG_OPTS, args);
while let Some(c) = w.wgetopt_long() {
match c {
'h' => {
opts.print_help = true;
}
'g' => {
opts.scope = Scope::Global;
}
'l' => {
opts.scope = Scope::Local;
}
'e' => {
opts.erase = true;
}
':' => {
builtin_missing_argument(parser, streams, cmd, args[w.woptind - 1], false);
return Err(STATUS_INVALID_ARGS);
}
'?' => {
builtin_unknown_option(parser, streams, cmd, args[w.woptind - 1], false);
return Err(STATUS_INVALID_ARGS);
}
_ => {
panic!("unexpected retval from wgetopt_long");
}
}
}
Ok((opts, w.woptind))
}
/// The block builtin, used for temporarily blocking events.
pub fn block(
parser: &mut parser_t,
streams: &mut io_streams_t,
args: &mut [&wstr],
) -> Option<c_int> {
let cmd = args[0];
let opts = match parse_options(args, parser, streams) {
Ok((opts, _)) => opts,
Err(err @ Some(_)) if err != STATUS_CMD_OK => return err,
Err(err) => panic!("Illogical exit code from parse_options(): {err:?}"),
};
if opts.print_help {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
if opts.erase {
if opts.scope != Scope::Unset {
streams.err.append(wgettext_fmt!(
"%ls: Can not specify scope when removing block\n",
cmd
));
return STATUS_INVALID_ARGS;
}
if parser.ffi_global_event_blocks() == 0 {
streams
.err
.append(wgettext_fmt!("%ls: No blocks defined\n", cmd));
return STATUS_CMD_ERROR;
}
parser.pin().ffi_decr_global_event_blocks();
return STATUS_CMD_OK;
}
let mut block_idx = 0;
let mut block = unsafe { parser.pin().block_at_index1(block_idx).as_mut() };
match opts.scope {
Scope::Local => {
// If this is the outermost block, then we're global
if block_idx + 1 >= parser.ffi_blocks_size() {
block = None;
}
}
Scope::Global => {
block = None;
}
Scope::Unset => {
loop {
block = if let Some(block) = block.as_mut() {
if !block.is_function_call() {
break;
}
// Set it in function scope
block_idx += 1;
unsafe { parser.pin().block_at_index1(block_idx).as_mut() }
} else {
break;
}
}
}
}
if let Some(block) = block.as_mut() {
block.pin().ffi_incr_event_blocks();
} else {
parser.pin().ffi_incr_global_event_blocks();
}
return STATUS_CMD_OK;
}

View file

@ -1,6 +1,7 @@
pub mod shared;
pub mod abbr;
pub mod block;
pub mod contains;
pub mod echo;
pub mod emit;

View file

@ -119,6 +119,7 @@ pub fn run_builtin(
) -> Option<c_int> {
match builtin {
RustBuiltin::Abbr => super::abbr::abbr(parser, streams, args),
RustBuiltin::Block => super::block::block(parser, streams, args),
RustBuiltin::Contains => super::contains::contains(parser, streams, args),
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
RustBuiltin::Emit => super::emit::emit(parser, streams, args),

View file

@ -53,6 +53,7 @@ include_cpp! {
generate!("wildcard_match")
generate!("wgettext_ptr")
generate!("block_t")
generate!("parser_t")
generate!("job_t")
generate!("process_t")
@ -163,6 +164,7 @@ pub trait Repin {
}
// Implement Repin for our types.
impl Repin for block_t {}
impl Repin for env_stack_t {}
impl Repin for io_streams_t {}
impl Repin for job_t {}

View file

@ -32,7 +32,6 @@
#include "builtins/argparse.h"
#include "builtins/bg.h"
#include "builtins/bind.h"
#include "builtins/block.h"
#include "builtins/builtin.h"
#include "builtins/cd.h"
#include "builtins/command.h"
@ -364,7 +363,7 @@ static constexpr builtin_data_t builtin_datas[] = {
{L"begin", &builtin_generic, N_(L"Create a block of code")},
{L"bg", &builtin_bg, N_(L"Send job to background")},
{L"bind", &builtin_bind, N_(L"Handle fish key bindings")},
{L"block", &builtin_block, N_(L"Temporarily block delivery of events")},
{L"block", &implemented_in_rust, N_(L"Temporarily block delivery of events")},
{L"break", &builtin_break_continue, N_(L"Stop the innermost loop")},
{L"breakpoint", &builtin_breakpoint, N_(L"Halt execution and start debug prompt")},
{L"builtin", &builtin_builtin, N_(L"Run a builtin specifically")},
@ -525,6 +524,9 @@ static maybe_t<RustBuiltin> try_get_rust_builtin(const wcstring &cmd) {
if (cmd == L"abbr") {
return RustBuiltin::Abbr;
}
if (cmd == L"block") {
return RustBuiltin::Block;
}
if (cmd == L"contains") {
return RustBuiltin::Contains;
}

View file

@ -110,6 +110,7 @@ int parse_help_only_cmd_opts(help_only_cmd_opts_t &opts, int *optind, int argc,
/// An enum of the builtins implemented in Rust.
enum RustBuiltin : int32_t {
Abbr,
Block,
Contains,
Echo,
Emit,

View file

@ -1,135 +0,0 @@
// Implementation of the block builtin.
#include "config.h" // IWYU pragma: keep
#include "block.h"
#include <cstddef>
#include <deque>
#include "../builtin.h"
#include "../common.h"
#include "../fallback.h" // IWYU pragma: keep
#include "../io.h"
#include "../maybe.h"
#include "../parser.h"
#include "../wgetopt.h"
#include "../wutil.h" // IWYU pragma: keep
enum { UNSET, GLOBAL, LOCAL };
struct block_cmd_opts_t {
int scope = UNSET;
bool erase = false;
bool print_help = false;
};
static int parse_cmd_opts(block_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method)
int argc, const wchar_t **argv, parser_t &parser, io_streams_t &streams) {
const wchar_t *cmd = argv[0];
static const wchar_t *const short_options = L":eghl";
static const struct woption long_options[] = {{L"erase", no_argument, 'e'},
{L"local", no_argument, 'l'},
{L"global", no_argument, 'g'},
{L"help", no_argument, 'h'},
{}};
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, nullptr)) != -1) {
switch (opt) {
case 'h': {
opts.print_help = true;
break;
}
case 'g': {
opts.scope = GLOBAL;
break;
}
case 'l': {
opts.scope = LOCAL;
break;
}
case 'e': {
opts.erase = true;
break;
}
case ':': {
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
case '?': {
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
}
}
}
*optind = w.woptind;
return STATUS_CMD_OK;
}
/// The block builtin, used for temporarily blocking events.
maybe_t<int> builtin_block(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
const wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
block_cmd_opts_t opts;
int optind;
int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams);
if (retval != STATUS_CMD_OK) return retval;
if (opts.print_help) {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
if (opts.erase) {
if (opts.scope != UNSET) {
streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), cmd);
return STATUS_INVALID_ARGS;
}
if (!parser.global_event_blocks) {
streams.err.append_format(_(L"%ls: No blocks defined\n"), cmd);
return STATUS_CMD_ERROR;
}
--parser.global_event_blocks;
return STATUS_CMD_OK;
}
size_t block_idx = 0;
block_t *block = parser.block_at_index(block_idx);
switch (opts.scope) {
case LOCAL: {
// If this is the outermost block, then we're global
if (block_idx + 1 >= parser.blocks().size()) {
block = nullptr;
}
break;
}
case GLOBAL: {
block = nullptr;
break;
}
case UNSET: {
while (block && !block->is_function_call()) {
// Set it in function scope
block = parser.block_at_index(++block_idx);
}
break;
}
default: {
DIE("unexpected scope");
}
}
if (block) {
++block->event_blocks;
} else {
++parser.global_event_blocks;
}
return STATUS_CMD_OK;
}

View file

@ -1,11 +0,0 @@
// Prototypes for executing builtin_block function.
#ifndef FISH_BUILTIN_BLOCK_H
#define FISH_BUILTIN_BLOCK_H
#include "../maybe.h"
class parser_t;
struct io_streams_t;
maybe_t<int> builtin_block(parser_t &parser, io_streams_t &streams, const wchar_t **argv);
#endif

View file

@ -680,6 +680,12 @@ bool parser_t::ffi_has_funtion_block() const {
return false;
}
uint64_t parser_t::ffi_global_event_blocks() const { return global_event_blocks; }
void parser_t::ffi_incr_global_event_blocks() { ++global_event_blocks; }
void parser_t::ffi_decr_global_event_blocks() { --global_event_blocks; }
size_t parser_t::ffi_blocks_size() const { return block_list.size(); }
block_t::block_t(block_type_t t) : block_type(t) {}
wcstring block_t::description() const {
@ -748,6 +754,10 @@ wcstring block_t::description() const {
return result;
}
bool block_t::is_function_call() const {
return type() == block_type_t::function_call || type() == block_type_t::function_call_no_shadow;
}
// Various block constructors.
block_t block_t::if_block() { return block_t(block_type_t::if_block); }
@ -782,3 +792,7 @@ block_t block_t::scope_block(block_type_t type) {
}
block_t block_t::breakpoint_block() { return block_t(block_type_t::breakpoint); }
block_t block_t::variable_assignment_block() { return block_t(block_type_t::variable_assignment); }
void block_t::ffi_incr_event_blocks() {
++event_blocks;
}

View file

@ -97,10 +97,7 @@ class block_t {
block_type_t type() const { return this->block_type; }
/// \return if we are a function call (with or without shadowing).
bool is_function_call() const {
return type() == block_type_t::function_call ||
type() == block_type_t::function_call_no_shadow;
}
bool is_function_call() const;
/// Entry points for creating blocks.
static block_t if_block();
@ -113,6 +110,9 @@ class block_t {
static block_t scope_block(block_type_t type);
static block_t breakpoint_block();
static block_t variable_assignment_block();
/// autocxx junk.
void ffi_incr_event_blocks();
};
struct profile_item_t {
@ -484,6 +484,14 @@ class parser_t : public std::enable_shared_from_this<parser_t> {
/// autocxx junk.
bool ffi_has_funtion_block() const;
/// autocxx junk.
uint64_t ffi_global_event_blocks() const;
void ffi_incr_global_event_blocks();
void ffi_decr_global_event_blocks();
/// autocxx junk.
size_t ffi_blocks_size() const;
~parser_t();
};