Skip to content

Commit 8787e8e

Browse files
committed
wip
1 parent 1a60160 commit 8787e8e

File tree

8 files changed

+134
-114
lines changed

8 files changed

+134
-114
lines changed

build.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub fn build(b: *std.Build) !void {
2222
const superhtml = b.addModule("superhtml", .{
2323
.root_source_file = b.path("src/root.zig"),
2424
.target = target,
25+
.optimize = optimize,
2526
});
2627
superhtml.addImport("scripty", scripty.module("scripty"));
2728
superhtml.addImport("tracy", tracy.module("tracy"));

src/cli/check.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const super = @import("superhtml");
33

44
const FileType = enum { html, super };
55

6-
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
6+
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !noreturn {
77
const cmd = Command.parse(args);
88
var any_error = false;
99
switch (cmd.mode) {
@@ -64,6 +64,7 @@ pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
6464
if (any_error) {
6565
std.process.exit(1);
6666
}
67+
std.process.exit(0);
6768
}
6869

6970
fn checkDir(

src/cli/fmt.zig

Lines changed: 102 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,69 @@
11
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
const Writer = std.Io.Writer;
24
const super = @import("superhtml");
35

4-
const FileType = enum { html, super };
6+
var bufout: [4096]u8 = undefined;
7+
var buferr: [4096]u8 = undefined;
58

6-
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
7-
const cmd = Command.parse(args);
8-
var any_error = false;
9-
switch (cmd.mode) {
10-
.stdin => {
11-
var fr = std.fs.File.stdin().reader(&.{});
12-
var aw: std.Io.Writer.Allocating = .init(gpa);
13-
_ = try fr.interface.streamRemaining(&aw.writer);
14-
const in_bytes = try aw.toOwnedSliceSentinel(0);
9+
var syntax_errors = false;
10+
pub fn run(gpa: Allocator, args: []const []const u8) !noreturn {
11+
// Prints html errors found in the document
12+
var stderr_writer = std.fs.File.stderr().writerStreaming(&buferr);
13+
const stderr = &stderr_writer.interface;
1514

16-
const out_bytes = try fmtHtml(gpa, null, in_bytes, cmd.strict);
15+
// Prints file paths of files that were modified on disk
16+
var stdout_writer = std.fs.File.stdout().writerStreaming(&bufout);
17+
const stdout = &stdout_writer.interface;
1718

18-
try std.fs.File.stdout().writeAll(out_bytes);
19-
},
20-
.stdin_super => {
19+
const cmd = Command.parse(args);
20+
switch (cmd.mode) {
21+
.stdin, .stdin_super => {
2122
var fr = std.fs.File.stdin().reader(&.{});
22-
var aw: std.Io.Writer.Allocating = .init(gpa);
23+
var aw: Writer.Allocating = .init(gpa);
2324
_ = try fr.interface.streamRemaining(&aw.writer);
2425
const in_bytes = try aw.toOwnedSliceSentinel(0);
25-
26-
const out_bytes = try fmtSuper(gpa, null, in_bytes, cmd.strict);
27-
try std.fs.File.stdout().writeAll(out_bytes);
26+
const lang: super.Language = switch (cmd.mode) {
27+
else => unreachable,
28+
.stdin => .html,
29+
.stdin_super => .superhtml,
30+
};
31+
32+
if (try fmt(gpa, stderr, null, in_bytes, lang, cmd.strict)) |fmt_src| {
33+
try std.fs.File.stdout().writeAll(fmt_src);
34+
}
2835
},
2936
.paths => |paths| {
3037
// checkFile will reset the arena at the end of each call
3138
var arena_impl = std.heap.ArenaAllocator.init(gpa);
3239
for (paths) |path| {
3340
formatFile(
3441
&arena_impl,
42+
stdout,
43+
stderr,
3544
cmd.check,
3645
std.fs.cwd(),
3746
path,
3847
path,
39-
&any_error,
4048
cmd.strict,
4149
) catch |err| switch (err) {
42-
error.IsDir, error.AccessDenied => {
43-
formatDir(
44-
gpa,
45-
&arena_impl,
46-
cmd.check,
50+
error.IsDir, error.AccessDenied => formatDir(
51+
gpa,
52+
&arena_impl,
53+
stdout,
54+
stderr,
55+
cmd.check,
56+
path,
57+
cmd.strict,
58+
) catch |dir_err| {
59+
std.debug.print("error walking dir '{s}': {s}\n", .{
4760
path,
48-
&any_error,
49-
cmd.strict,
50-
) catch |dir_err| {
51-
std.debug.print("Error walking dir '{s}': {s}\n", .{
52-
path,
53-
@errorName(dir_err),
54-
});
55-
std.process.exit(1);
56-
};
61+
@errorName(dir_err),
62+
});
63+
std.process.exit(1);
5764
},
5865
else => {
59-
std.debug.print("Error while accessing '{s}': {s}\n", .{
66+
std.debug.print("error while accessing '{s}': {s}\n", .{
6067
path, @errorName(err),
6168
});
6269
std.process.exit(1);
@@ -66,48 +73,51 @@ pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
6673
},
6774
}
6875

69-
if (any_error) {
70-
std.process.exit(1);
71-
}
76+
try stdout.flush();
77+
try stderr.flush();
78+
std.process.exit(@intFromBool(syntax_errors));
7279
}
7380

7481
fn formatDir(
7582
gpa: std.mem.Allocator,
7683
arena_impl: *std.heap.ArenaAllocator,
84+
stdout: *Writer,
85+
stderr: *Writer,
7786
check: bool,
7887
path: []const u8,
79-
any_error: *bool,
8088
strict: bool,
8189
) !void {
8290
var dir = try std.fs.cwd().openDir(path, .{ .iterate = true });
8391
defer dir.close();
92+
8493
var walker = dir.walk(gpa) catch oom();
8594
defer walker.deinit();
95+
8696
while (try walker.next()) |item| {
8797
switch (item.kind) {
88-
.file => {
89-
try formatFile(
90-
arena_impl,
91-
check,
92-
item.dir,
93-
item.basename,
94-
item.path,
95-
any_error,
96-
strict,
97-
);
98-
},
98+
.file => try formatFile(
99+
arena_impl,
100+
stdout,
101+
stderr,
102+
check,
103+
item.dir,
104+
item.basename,
105+
item.path,
106+
strict,
107+
),
99108
else => {},
100109
}
101110
}
102111
}
103112

104113
fn formatFile(
105114
arena_impl: *std.heap.ArenaAllocator,
115+
stdout: *Writer,
116+
stderr: *Writer,
106117
check: bool,
107118
base_dir: std.fs.Dir,
108119
sub_path: []const u8,
109120
full_path: []const u8,
110-
any_error: *bool,
111121
strict: bool,
112122
) !void {
113123
defer _ = arena_impl.reset(.retain_capacity);
@@ -121,7 +131,7 @@ fn formatFile(
121131
0,
122132
);
123133

124-
const file_type: FileType = blk: {
134+
const language: super.Language = blk: {
125135
const ext = std.fs.path.extension(sub_path);
126136
if (std.mem.eql(u8, ext, ".html") or
127137
std.mem.eql(u8, ext, ".htm"))
@@ -130,74 +140,60 @@ fn formatFile(
130140
}
131141

132142
if (std.mem.eql(u8, ext, ".shtml")) {
133-
break :blk .super;
143+
break :blk .superhtml;
134144
}
145+
146+
// Unkown file, skip it
135147
return;
136148
};
137149

138-
const out_bytes = switch (file_type) {
139-
.html => try fmtHtml(
140-
arena,
141-
full_path,
142-
in_bytes,
143-
strict,
144-
),
145-
.super => try fmtSuper(
146-
arena,
147-
full_path,
148-
in_bytes,
149-
strict,
150-
),
151-
};
150+
if (try fmt(arena, stderr, full_path, in_bytes, language, strict)) |fmt_src| {
151+
if (std.mem.eql(u8, fmt_src, in_bytes)) return;
152+
if (check) {
153+
syntax_errors = true;
154+
try stdout.print("{s}\n", .{full_path});
155+
return;
156+
}
152157

153-
if (std.mem.eql(u8, out_bytes, in_bytes)) return;
158+
var af = try base_dir.atomicFile(sub_path, .{ .write_buffer = &.{} });
159+
defer af.deinit();
154160

155-
var stdout_writer = std.fs.File.stdout().writer(&.{});
156-
const stdout = &stdout_writer.interface;
157-
if (check) {
158-
any_error.* = true;
161+
try af.file_writer.interface.writeAll(fmt_src);
162+
try af.finish();
163+
try stdout.print("{s}\n", .{full_path});
164+
} else if (check) {
165+
syntax_errors = true;
166+
// if (true) @panic("hit");
159167
try stdout.print("{s}\n", .{full_path});
160168
return;
161169
}
162-
163-
var af = try base_dir.atomicFile(sub_path, .{ .write_buffer = &.{} });
164-
defer af.deinit();
165-
166-
try af.file_writer.interface.writeAll(out_bytes);
167-
try af.finish();
168-
try stdout.print("{s}\n", .{full_path});
169170
}
170171

171-
pub fn fmtHtml(
172+
pub fn fmt(
172173
arena: std.mem.Allocator,
174+
stderr: *Writer,
173175
path: ?[]const u8,
174-
code: [:0]const u8,
176+
src: [:0]const u8,
177+
language: super.Language,
175178
strict: bool,
176-
) ![]const u8 {
177-
const ast = try super.html.Ast.init(arena, code, .html, strict);
178-
if (ast.errors.len > 0) {
179-
var ew = std.fs.File.stderr().writer(&.{});
180-
try ast.printErrors(code, path, &ew.interface);
181-
std.process.exit(1);
182-
}
183-
184-
return std.fmt.allocPrint(arena, "{f}", .{ast.formatter(code)});
185-
}
186-
187-
fn fmtSuper(
188-
arena: std.mem.Allocator,
189-
path: ?[]const u8,
190-
code: [:0]const u8,
191-
strict: bool,
192-
) ![]const u8 {
193-
const ast = try super.html.Ast.init(arena, code, .superhtml, strict);
194-
if (ast.errors.len > 0) {
195-
var ew = std.fs.File.stderr().writer(&.{});
196-
try ast.printErrors(code, path, &ew.interface);
197-
std.process.exit(1);
179+
) !?[]const u8 {
180+
const html_ast = try super.html.Ast.init(arena, src, language, strict);
181+
if (html_ast.errors.len > 0) {
182+
try html_ast.printErrors(src, path, stderr);
183+
if (html_ast.has_syntax_errors) {
184+
syntax_errors = true;
185+
return null;
186+
}
187+
} else if (language == .superhtml) {
188+
const super_ast = try super.Ast.init(arena, html_ast, src);
189+
if (super_ast.errors.len > 0) {
190+
try html_ast.printErrors(src, path, stderr);
191+
}
198192
}
199193

200-
return std.fmt.allocPrint(arena, "{f}", .{ast.formatter(code)});
194+
return try std.fmt.allocPrint(arena, "{f}", .{
195+
html_ast.formatter(src),
196+
});
201197
}
202198

203199
fn oom() noreturn {
@@ -301,6 +297,9 @@ const Command = struct {
301297
\\
302298
\\ Formats input paths inplace. If PATH is a directory, it will
303299
\\ be searched recursively for HTML and SuperHTML files.
300+
\\ HTML errors will be printed to stderr but will only cause a
301+
\\ non-zero exit code if they prevent formatting (e.g. syntax
302+
\\ errors).
304303
\\
305304
\\ Detected extensions:
306305
\\ HTML .html, .htm

src/cli/interface.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const super = @import("superhtml");
33

44
const FileType = enum { html, super };
55

6-
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
6+
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !noreturn {
77
const cmd = Command.parse(args);
88
switch (cmd.mode) {
99
.stdin => {
@@ -41,6 +41,8 @@ pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
4141
try std.fs.File.stdout().writeAll(out_bytes);
4242
},
4343
}
44+
45+
std.process.exit(0);
4446
}
4547

4648
fn printInterfaceFromFile(

src/cli/logging.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn logFn(
2323
args: anytype,
2424
) void {
2525
switch (scope) {
26-
.root, .super_lsp, .@"html/ast" => {},
26+
.root, .super_lsp, .@"html/ast/fmt" => {},
2727
else => return,
2828
}
2929
// inline for (build_options.enabled_scopes) |es| {

src/cli/lsp.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const logic = @import("lsp/logic.zig");
1111

1212
const log = std.log.scoped(.super_lsp);
1313

14-
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
14+
pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !noreturn {
1515
_ = args;
1616

1717
log.debug("SuperHTML LSP started!", .{});
@@ -36,6 +36,8 @@ pub fn run(gpa: std.mem.Allocator, args: []const []const u8) !void {
3636
&handler,
3737
log.err,
3838
);
39+
40+
std.process.exit(0);
3941
}
4042

4143
pub const Handler = @This();

0 commit comments

Comments
 (0)