lib/pretty/core/src/builder.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const types = @import("types.zig");
3
4 const Doc = types.Doc;
5 const Style = types.Style;
6 const hardline = types.hardline;
7 const softline = types.softline;
8
9 pub const Builder = struct {
10 allocator: std.mem.Allocator,
11
12 pub fn init(allocator: std.mem.Allocator) Builder {
13 return .{ .allocator = allocator };
14 }
15
16 pub fn empty(_: Builder) Doc {
17 return .empty;
18 }
19
20 pub fn text(_: Builder, bytes: []const u8) Doc {
21 if (bytes.len == 0) return .empty;
22 return .{ .text = bytes };
23 }
24
25 pub fn textFmt(
26 self: Builder,
27 comptime fmt: []const u8,
28 args: anytype,
29 ) std.mem.Allocator.Error!Doc {
30 return .{ .text = try std.fmt.allocPrint(self.allocator, fmt, args) };
31 }
32
33 pub fn spaces(
34 self: Builder,
35 amount: usize,
36 ) std.mem.Allocator.Error!Doc {
37 if (amount == 0) return self.empty();
38 const bytes = try self.allocator.alloc(u8, amount);
39 @memset(bytes, ' ');
40 return self.text(bytes);
41 }
42
43 pub fn concat(
44 self: Builder,
45 parts: []const Doc,
46 ) std.mem.Allocator.Error!Doc {
47 if (parts.len == 0) return .empty;
48 if (parts.len == 1) return parts[0];
49 return .{ .concat = try self.allocator.dupe(Doc, parts) };
50 }
51
52 pub fn nest(
53 self: Builder,
54 amount: usize,
55 doc: Doc,
56 ) std.mem.Allocator.Error!Doc {
57 if (amount == 0) return doc;
58 const owned = try self.allocator.create(Doc);
59 owned.* = doc;
60 return .{ .nest = .{ .amount = amount, .doc = owned } };
61 }
62
63 pub fn group(
64 self: Builder,
65 doc: Doc,
66 ) std.mem.Allocator.Error!Doc {
67 const owned = try self.allocator.create(Doc);
68 owned.* = doc;
69 return .{ .group = owned };
70 }
71
72 pub fn style(
73 self: Builder,
74 style_kind: Style,
75 doc: Doc,
76 ) std.mem.Allocator.Error!Doc {
77 if (style_kind == .plain) return doc;
78 const owned = try self.allocator.create(Doc);
79 owned.* = doc;
80 return .{ .styled = .{ .style = style_kind, .doc = owned } };
81 }
82
83 pub fn styledText(
84 self: Builder,
85 style_kind: Style,
86 bytes: []const u8,
87 ) std.mem.Allocator.Error!Doc {
88 return try self.style(style_kind, self.text(bytes));
89 }
90
91 pub fn styledFmt(
92 self: Builder,
93 style_kind: Style,
94 comptime fmt: []const u8,
95 args: anytype,
96 ) std.mem.Allocator.Error!Doc {
97 return try self.style(style_kind, try self.textFmt(fmt, args));
98 }
99
100 pub fn words(
101 self: Builder,
102 bytes: []const u8,
103 ) std.mem.Allocator.Error!Doc {
104 var parts: std.ArrayListUnmanaged(Doc) = .empty;
105 defer parts.deinit(self.allocator);
106
107 var line_start = true;
108 var index: usize = 0;
109 while (index < bytes.len) {
110 while (index < bytes.len) : (index += 1) {
111 switch (bytes[index]) {
112 ' ', '\t', '\r' => {},
113 '\n' => {
114 try parts.append(self.allocator, hardline);
115 line_start = true;
116 },
117 else => break,
118 }
119 }
120
121 if (index >= bytes.len) break;
122
123 const word_start = index;
124 while (index < bytes.len and bytes[index] != ' ' and bytes[index] != '\t' and bytes[index] != '\r' and bytes[index] != '\n') : (index += 1) {}
125 const word = self.text(bytes[word_start..index]);
126
127 if (line_start) {
128 try parts.append(self.allocator, word);
129 line_start = false;
130 } else {
131 try parts.append(self.allocator, try self.group(try self.concat(&.{
132 softline,
133 word,
134 })));
135 }
136 }
137
138 return try self.concat(parts.items);
139 }
140
141 pub fn styledWords(
142 self: Builder,
143 style_kind: Style,
144 bytes: []const u8,
145 ) std.mem.Allocator.Error!Doc {
146 return try self.style(style_kind, try self.words(bytes));
147 }
148
149 pub fn punct(
150 self: Builder,
151 bytes: []const u8,
152 ) std.mem.Allocator.Error!Doc {
153 return try self.styledText(.punctuation, bytes);
154 }
155
156 pub fn join(
157 self: Builder,
158 items: []const Doc,
159 separator: Doc,
160 ) std.mem.Allocator.Error!Doc {
161 if (items.len == 0) return self.empty();
162 if (items.len == 1) return items[0];
163
164 const parts = try self.allocator.alloc(Doc, items.len * 2 - 1);
165 var index: usize = 0;
166 for (items, 0..) |item, item_index| {
167 if (item_index != 0) {
168 parts[index] = separator;
169 index += 1;
170 }
171 parts[index] = item;
172 index += 1;
173 }
174 return .{ .concat = parts };
175 }
176 };