aboutsummaryrefslogtreecommitdiff
path: root/src/string_immutable.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/string_immutable.zig')
-rw-r--r--src/string_immutable.zig73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 2cd70a6bb..1a21ae7c3 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -64,6 +64,79 @@ pub fn indexOfCharNeg(self: string, char: u8) i32 {
return -1;
}
+/// Format a string to an ECMAScript identifier.
+/// Unlike the string_mutable.zig version, this always allocate/copy
+pub fn fmtIdentifier(name: string) FormatValidIdentifier {
+ return FormatValidIdentifier{ .name = name };
+}
+
+/// Format a string to an ECMAScript identifier.
+/// Different implementation than string_mutable because string_mutable may avoid allocating
+/// This will always allocate
+pub const FormatValidIdentifier = struct {
+ name: string,
+ const js_lexer = @import("./js_lexer.zig");
+ pub fn format(self: FormatValidIdentifier, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ var iterator = strings.CodepointIterator.init(self.name);
+ var cursor = strings.CodepointIterator.Cursor{};
+
+ var has_needed_gap = false;
+ var needs_gap = false;
+ var start_i: usize = 0;
+
+ if (!iterator.next(&cursor)) {
+ try writer.writeAll("_");
+ return;
+ }
+
+ // Common case: no gap necessary. No allocation necessary.
+ needs_gap = !js_lexer.isIdentifierStart(cursor.c);
+ if (!needs_gap) {
+ // Are there any non-alphanumeric chars at all?
+ while (iterator.next(&cursor)) {
+ if (!js_lexer.isIdentifierContinue(cursor.c) or cursor.width > 1) {
+ needs_gap = true;
+ start_i = cursor.i;
+ break;
+ }
+ }
+ }
+
+ if (needs_gap) {
+ needs_gap = false;
+
+ var slice = self.name[start_i..];
+ iterator = strings.CodepointIterator.init(slice);
+ cursor = strings.CodepointIterator.Cursor{};
+
+ while (iterator.next(&cursor)) {
+ if (js_lexer.isIdentifierContinue(cursor.c) and cursor.width == 1) {
+ if (needs_gap) {
+ try writer.writeAll("_");
+ needs_gap = false;
+ has_needed_gap = true;
+ }
+ try writer.writeAll(slice[cursor.i .. cursor.i + @as(u32, cursor.width)]);
+ } else if (!needs_gap) {
+ needs_gap = true;
+ // skip the code point, replace it with a single _
+ }
+ }
+
+ // If it ends with an emoji
+ if (needs_gap) {
+ try writer.writeAll("_");
+ needs_gap = false;
+ has_needed_gap = true;
+ }
+
+ return;
+ }
+
+ try writer.writeAll(self.name);
+ }
+};
+
pub fn indexOfSigned(self: string, str: string) i32 {
const i = std.mem.indexOf(u8, self, str) orelse return -1;
return @intCast(i32, i);