Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

parseAcceleratorKeyString

Expects bytes to be the full bytes of a string literal token (e.g. including the "" or L"").

res.parseAcceleratorKeyString
pub fn parseAcceleratorKeyString(bytes: SourceBytes, is_virt: bool, options: literals.StringParseOptions) (ParseAcceleratorKeyStringError || Allocator.Error)!u16

File

Code

pub fn parseAcceleratorKeyString(bytes: SourceBytes, is_virt: bool, options: literals.StringParseOptions) (ParseAcceleratorKeyStringError || Allocator.Error)!u16 {
    if (bytes.slice.len == 0) {
        return error.EmptyAccelerator;
    }

    var parser = literals.IterativeStringParser.init(bytes, options);
    var translator = AcceleratorKeyCodepointTranslator{
        .string_type = parser.declared_string_type,
        .output_code_page = options.output_code_page,
    };

    const first_codepoint = translator.translate(try parser.next()) orelse return error.EmptyAccelerator;
    // 0 is treated as a terminator, so this is equivalent to an empty string
    if (first_codepoint == 0) return error.EmptyAccelerator;

    if (first_codepoint == '^') {
        // Note: Emitting this warning unconditionally whenever ^ is the first character
        //       matches the Win32 RC behavior, but it's questionable whether or not
        //       the warning should be emitted for ^^ since that results in the ASCII
        //       character ^ being written to the .res.
        if (is_virt and options.diagnostics != null) {
            try options.diagnostics.?.diagnostics.append(.{
                .err = .ascii_character_not_equivalent_to_virtual_key_code,
                .type = .warning,
                .code_page = bytes.code_page,
                .token = options.diagnostics.?.token,
            });
        }

        const c = translator.translate(try parser.next()) orelse return error.InvalidControlCharacter;

        const third_codepoint = translator.translate(try parser.next());
        // 0 is treated as a terminator, so a 0 in the third position is fine but
        // anything else is too many codepoints for an accelerator
        if (third_codepoint != null and third_codepoint.? != 0) return error.InvalidControlCharacter;

        switch (c) {
            '^' => return '^', // special case
            'a'...'z', 'A'...'Z' => return std.ascii.toUpper(@intCast(c)) - 0x40,
            // Note: The Windows RC compiler allows more than just A-Z, but what it allows
            //       seems to be tied to some sort of Unicode-aware 'is character' function or something.
            //       The full list of codepoints that trigger an out-of-range error can be found here:
            //       https://gist.github.com/squeek502/2e9d0a4728a83eed074ad9785a209fd0
            //       For codepoints >= 0x80 that don't trigger the error, the Windows RC compiler takes the
            //       codepoint and does the `- 0x40` transformation as if it were A-Z which couldn't lead
            //       to anything useable, so there's no point in emulating that behavior--erroring for
            //       all non-[a-zA-Z] makes much more sense and is what was probably intended by the
            //       Windows RC compiler.
            else => return error.ControlCharacterOutOfRange,
        }
        @compileError("this should be unreachable");
    }

    const second_codepoint = translator.translate(try parser.next());

    var result: u32 = initial_value: {
        if (first_codepoint >= 0x10000) {
            if (second_codepoint != null and second_codepoint.? != 0) return error.AcceleratorTooLong;
            // No idea why it works this way, but this seems to match the Windows RC
            // behavior for codepoints >= 0x10000
            const low = @as(u16, @intCast(first_codepoint & 0x3FF)) + 0xDC00;
            const extra = (first_codepoint - 0x10000) / 0x400;
            break :initial_value low + extra * 0x100;
        }
        break :initial_value first_codepoint;
    };

    // 0 is treated as a terminator
    if (second_codepoint != null and second_codepoint.? == 0) return @truncate(result);

    const third_codepoint = translator.translate(try parser.next());
    // 0 is treated as a terminator, so a 0 in the third position is fine but
    // anything else is too many codepoints for an accelerator
    if (third_codepoint != null and third_codepoint.? != 0) return error.AcceleratorTooLong;

    if (second_codepoint) |c| {
        if (c >= 0x10000) return error.AcceleratorTooLong;
        result <<= 8;
        result += c;
    } else if (is_virt) {
        switch (result) {
            'a'...'z' => result -= 0x20, // toUpper
            else => {},
        }
    }
    return @truncate(result);
}