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 };