mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 21:44:16 +00:00
parent
330e8a86c7
commit
6f5be9bae4
11 changed files with 194 additions and 153 deletions
|
@ -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
|
||||
|
|
158
fish-rust/src/builtins/block.rs
Normal file
158
fish-rust/src/builtins/block.rs
Normal 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;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
pub mod shared;
|
||||
|
||||
pub mod abbr;
|
||||
pub mod block;
|
||||
pub mod contains;
|
||||
pub mod echo;
|
||||
pub mod emit;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
16
src/parser.h
16
src/parser.h
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue