/// Define a function. Calls into `function.cpp` to perform the heavy lifting of defining a
/// function.
int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args,
                     const wcstring &contents, int definition_line_offset) {
    // The wgetopt function expects 'function' as the first argument. Make a new wcstring_list with
    // that property. This is needed because this builtin has a different signature than the other
    // builtins.
    wcstring_list_t args = {L"function"};
    args.insert(args.end(), c_args.begin(), c_args.end());

    // Hackish const_cast matches the one in builtin_run.
    const null_terminated_array_t<wchar_t> argv_array(args);
    wchar_t **argv = const_cast<wchar_t **>(argv_array.get());
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);

    // A valid function name has to be the first argument.
    wcstring function_name;
    int retval = validate_function_name(argc, argv, function_name, cmd, streams);
    if (retval != STATUS_CMD_OK) return retval;
    argv++;
    argc--;

    function_cmd_opts_t opts;
    int optind;
    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, streams.err);
        return STATUS_CMD_OK;
    }

    if (argc != optind) {
        if (opts.named_arguments.size()) {
            for (int i = optind; i < argc; i++) {
                opts.named_arguments.push_back(argv[i]);
            }
        } else {
            streams.err.append_format(_(L"%ls: Unexpected positional argument '%ls'"), cmd,
                                      argv[optind]);
            return STATUS_INVALID_ARGS;
        }
    }

    // We have what we need to actually define the function.
    function_data_t d;
    d.name = function_name;
    if (!opts.description.empty()) d.description = opts.description;
    // d.description = opts.description;
    d.events.swap(opts.events);
    d.shadow_scope = opts.shadow_scope;
    d.named_arguments.swap(opts.named_arguments);
    d.inherit_vars.swap(opts.inherit_vars);

    for (size_t i = 0; i < d.events.size(); i++) {
        event_t &e = d.events.at(i);
        e.function_name = d.name;
    }

    d.definition = contents.c_str();
    function_add(d, parser, definition_line_offset);

    // Handle wrap targets by creating the appropriate completions.
    for (size_t w = 0; w < opts.wrap_targets.size(); w++) {
        complete_add_wrapper(function_name, opts.wrap_targets.at(w));
    }

    return STATUS_CMD_OK;
}
/// Define a function. Calls into `function.cpp` to perform the heavy lifting of defining a
/// function.
int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args,
                     const parsed_source_ref_t &source, tnode_t<grammar::job_list> body) {
    assert(source && "Missing source in builtin_function");
    // The wgetopt function expects 'function' as the first argument. Make a new wcstring_list with
    // that property. This is needed because this builtin has a different signature than the other
    // builtins.
    wcstring_list_t args = {L"function"};
    args.insert(args.end(), c_args.begin(), c_args.end());

    null_terminated_array_t<wchar_t> argv_array(args);
    wchar_t **argv = argv_array.get();
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);

    // A valid function name has to be the first argument.
    wcstring function_name;
    int retval = validate_function_name(argc, argv, function_name, cmd, streams);
    if (retval != STATUS_CMD_OK) return retval;
    argv++;
    argc--;

    function_cmd_opts_t opts;
    int optind;
    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, streams.err);
        return STATUS_CMD_OK;
    }

    if (argc != optind) {
        if (opts.named_arguments.size()) {
            for (int i = optind; i < argc; i++) {
                opts.named_arguments.push_back(argv[i]);
            }
        } else {
            streams.err.append_format(_(L"%ls: Unexpected positional argument '%ls'"), cmd,
                                      argv[optind]);
            return STATUS_INVALID_ARGS;
        }
    }

    // We have what we need to actually define the function.
    function_data_t d;
    d.name = function_name;
    if (!opts.description.empty()) d.description = opts.description;
    // d.description = opts.description;
    d.events.swap(opts.events);
    d.props.shadow_scope = opts.shadow_scope;
    d.props.named_arguments = std::move(opts.named_arguments);
    d.inherit_vars = std::move(opts.inherit_vars);

    for (size_t i = 0; i < d.events.size(); i++) {
        event_t &e = d.events.at(i);
        e.function_name = d.name;
    }

    d.props.parsed_source = source;
    d.props.body_node = body;
    function_add(std::move(d), parser);

    // Handle wrap targets by creating the appropriate completions.
    for (const wcstring &wt : opts.wrap_targets) complete_add_wrapper(function_name, wt);
    return STATUS_CMD_OK;
}