mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-24 16:11:43 +00:00
osc 99 & 5522: share metadata parsing code
Reduce redundant code by sharing the metadata parsing code between the OSC 99 & OSC 5522 parsers.
This commit is contained in:
committed by
Mitchell Hashimoto
parent
aa4ec3508e
commit
07f33ad914
61
src/terminal/osc/lib.zig
Normal file
61
src/terminal/osc/lib.zig
Normal file
@@ -0,0 +1,61 @@
|
||||
const std = @import("std");
|
||||
|
||||
/// Used to iterate over matching key/values in the metadata
|
||||
pub fn Iterator(comptime Option: type, comptime isValidMetadataValue: fn ([]const u8) bool, comptime key: Option) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
metadata: []const u8,
|
||||
pos: usize,
|
||||
|
||||
pub fn init(metadata: []const u8) Self {
|
||||
return .{
|
||||
.metadata = metadata,
|
||||
.pos = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Return the value of the next matching key. The value is guaranteed
|
||||
/// to be `null` or a valid metadata value.
|
||||
pub fn next(self: *Self) ?[]const u8 {
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
while (self.pos < self.metadata.len) {
|
||||
// skip any whitespace
|
||||
while (self.pos < self.metadata.len and std.ascii.isWhitespace(self.metadata[self.pos])) self.pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
if (!std.mem.startsWith(u8, self.metadata[self.pos..], @tagName(key))) {
|
||||
// this isn't the key we are looking for, skip to the next option, or bail if
|
||||
// there is no next option
|
||||
self.pos = std.mem.indexOfScalarPos(u8, self.metadata, self.pos, ':') orelse {
|
||||
self.pos = self.metadata.len;
|
||||
return null;
|
||||
};
|
||||
self.pos += 1;
|
||||
continue;
|
||||
}
|
||||
// skip past the key
|
||||
self.pos += @tagName(key).len;
|
||||
// skip any whitespace
|
||||
while (self.pos < self.metadata.len and std.ascii.isWhitespace(self.metadata[self.pos])) self.pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
// a valid option has an '='
|
||||
if (self.metadata[self.pos] != '=') return null;
|
||||
// the end of the value is bounded by a ':' or the end of the metadata
|
||||
const end = std.mem.indexOfScalarPos(u8, self.metadata, self.pos, ':') orelse self.metadata.len;
|
||||
const start = self.pos + 1;
|
||||
self.pos = end + 1;
|
||||
// strip any leading or trailing whitespace
|
||||
const value = std.mem.trim(u8, self.metadata[start..end], &std.ascii.whitespace);
|
||||
// if this is not a valid value, skip it
|
||||
if (!@call(.always_inline, isValidMetadataValue, .{value})) continue;
|
||||
// return the value
|
||||
return value;
|
||||
}
|
||||
// the key was not found
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -10,6 +10,7 @@ const assert = @import("../../../quirks.zig").inlineAssert;
|
||||
const Parser = @import("../../osc.zig").Parser;
|
||||
const Command = @import("../../osc.zig").Command;
|
||||
const Terminator = @import("../../osc.zig").Terminator;
|
||||
const Iterator = @import("../lib.zig").Iterator;
|
||||
const encoding = @import("../encoding.zig");
|
||||
|
||||
const log = std.log.scoped(.kitty_clipboard_protocol);
|
||||
@@ -96,37 +97,8 @@ pub const Option = enum {
|
||||
comptime key: Option,
|
||||
metadata: []const u8,
|
||||
) ?key.Type() {
|
||||
const value: []const u8 = value: {
|
||||
var pos: usize = 0;
|
||||
while (pos < metadata.len) {
|
||||
// skip any whitespace
|
||||
while (pos < metadata.len and std.ascii.isWhitespace(metadata[pos])) pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (pos >= metadata.len) return null;
|
||||
if (!std.mem.startsWith(u8, metadata[pos..], @tagName(key))) {
|
||||
// this isn't the key we are looking for, skip to the next option, or bail if
|
||||
// there is no next option
|
||||
pos = std.mem.indexOfScalarPos(u8, metadata, pos, ':') orelse return null;
|
||||
pos += 1;
|
||||
continue;
|
||||
}
|
||||
// skip past the key
|
||||
pos += @tagName(key).len;
|
||||
// skip any whitespace
|
||||
while (pos < metadata.len and std.ascii.isWhitespace(metadata[pos])) pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (pos >= metadata.len) return null;
|
||||
// a valid option has an '='
|
||||
if (metadata[pos] != '=') return null;
|
||||
// the end of the value is bounded by a ':' or the end of the metadata
|
||||
const end = std.mem.indexOfScalarPos(u8, metadata, pos, ':') orelse metadata.len;
|
||||
const start = pos + 1;
|
||||
// strip any leading or trailing whitespace
|
||||
break :value std.mem.trim(u8, metadata[start..end], &std.ascii.whitespace);
|
||||
}
|
||||
// the key was not found
|
||||
return null;
|
||||
};
|
||||
var it: Iterator(Option, isValidMetadataValue, key) = .init(metadata);
|
||||
const value = it.next() orelse return null;
|
||||
|
||||
// return the parsed value
|
||||
return switch (key) {
|
||||
@@ -155,6 +127,10 @@ fn parseIdentifier(str: []const u8) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn isValidMetadataValue(_: []const u8) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn parse(parser: *Parser, terminator_ch: ?u8) ?*Command {
|
||||
assert(parser.state == .@"5522");
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const assert = @import("../../../quirks.zig").inlineAssert;
|
||||
const Parser = @import("../../osc.zig").Parser;
|
||||
const Command = @import("../../osc.zig").Command;
|
||||
const Terminator = @import("../../osc.zig").Terminator;
|
||||
const Iterator = @import("../lib.zig").Iterator;
|
||||
const encoding = @import("../encoding.zig");
|
||||
const lib = @import("../../../lib/main.zig");
|
||||
const lib_target: lib.Target = if (build_options.c_abi) .c else .zig;
|
||||
@@ -136,11 +137,11 @@ pub const Option = enum {
|
||||
.f => ?[]const u8,
|
||||
.g => ?[]const u8,
|
||||
.i => ?[]const u8,
|
||||
.n => Iterator(.n),
|
||||
.n => Iterator(Option, isValidMetadataValue, .n),
|
||||
.o => Occasion,
|
||||
.p => Payload,
|
||||
.s => []const u8,
|
||||
.t => Iterator(.t),
|
||||
.t => Iterator(Option, isValidMetadataValue, .t),
|
||||
.u => Urgency,
|
||||
.w => i32,
|
||||
};
|
||||
@@ -173,7 +174,7 @@ pub const Option = enum {
|
||||
comptime key: Option,
|
||||
metadata: []const u8,
|
||||
) key.Type() {
|
||||
var it: Iterator(key) = switch (key) {
|
||||
var it: Iterator(Option, isValidMetadataValue, key) = switch (key) {
|
||||
inline .t, .n => return .init(metadata),
|
||||
inline else => .init(metadata),
|
||||
};
|
||||
@@ -279,64 +280,6 @@ fn parseIdentifier(str: []const u8) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Used to iterate over matching key/values in the metadata
|
||||
pub fn Iterator(comptime key: Option) type {
|
||||
return struct {
|
||||
metadata: []const u8,
|
||||
pos: usize,
|
||||
|
||||
pub fn init(metadata: []const u8) Iterator(key) {
|
||||
return .{
|
||||
.metadata = metadata,
|
||||
.pos = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Return the value of the next matching key. The value is guaranteed
|
||||
/// to be `null` or a valid metadata value.
|
||||
pub fn next(self: *Iterator(key)) ?[]const u8 {
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
while (self.pos < self.metadata.len) {
|
||||
// skip any whitespace
|
||||
while (self.pos < self.metadata.len and std.ascii.isWhitespace(self.metadata[self.pos])) self.pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
if (self.metadata[self.pos] != @tagName(key)[0]) {
|
||||
// this isn't the key we are looking for, skip to the next option, or bail if
|
||||
// there is no next option
|
||||
self.pos = std.mem.indexOfScalarPos(u8, self.metadata, self.pos, ':') orelse {
|
||||
self.pos = self.metadata.len;
|
||||
return null;
|
||||
};
|
||||
self.pos += 1;
|
||||
continue;
|
||||
}
|
||||
// skip past the key
|
||||
self.pos += 1;
|
||||
// skip any whitespace
|
||||
while (self.pos < self.metadata.len and std.ascii.isWhitespace(self.metadata[self.pos])) self.pos += 1;
|
||||
// bail if we are out of metadata
|
||||
if (self.pos >= self.metadata.len) return null;
|
||||
// a valid option has an '='
|
||||
if (self.metadata[self.pos] != '=') return null;
|
||||
// the end of the value is bounded by a ':' or the end of the metadata
|
||||
const end = std.mem.indexOfScalarPos(u8, self.metadata, self.pos, ':') orelse self.metadata.len;
|
||||
const start = self.pos + 1;
|
||||
self.pos = end + 1;
|
||||
// strip any leading or trailing whitespace
|
||||
const value = std.mem.trim(u8, self.metadata[start..end], &std.ascii.whitespace);
|
||||
// if this is not a valid value, skip it
|
||||
if (!isValidMetadataValue(value)) continue;
|
||||
// return the value
|
||||
return value;
|
||||
}
|
||||
// the key was not found
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parse(parser: *Parser, terminator_ch: ?u8) ?*Command {
|
||||
assert(parser.state == .@"99");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user