Skip to content

Commit 1568aa0

Browse files
ospencerphated
andauthored
feat(grainfmt)!: Implement new formatter (#1976)
Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com>
1 parent e522453 commit 1568aa0

File tree

142 files changed

+8652
-9680
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+8652
-9680
lines changed

compiler/grainformat/grainformat.re

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let get_program_string = filename => {
2828
let compile_parsed = filename => {
2929
let filename = Filepath.to_string(filename);
3030
let program_str = get_program_string(filename);
31-
switch (Format.parse_source(program_str)) {
31+
switch (Fmt.parse_source(program_str)) {
3232
| Error(ParseError(exn)) =>
3333
let bt =
3434
if (Printexc.backtrace_status()) {
@@ -56,28 +56,28 @@ let format_code =
5656
(
5757
~eol,
5858
~output=?,
59-
~original_source: array(string),
59+
~source: array(string),
6060
program: Parsetree.parsed_program,
6161
) => {
62-
let formatted_code =
63-
Grain_formatting.Format.format_ast(~original_source, ~eol, program);
64-
65-
let buf = Buffer.create(0);
66-
Buffer.add_string(buf, formatted_code);
67-
68-
let contents = Buffer.to_bytes(buf);
6962
switch (output) {
7063
| Some(outfile) =>
7164
let outfile = Filepath.to_string(outfile);
7265
// TODO: This crashes if you do something weird like `-o stdout/map.gr/foo`
7366
// because `foo` doesn't exist so it tries to mkdir it and raises
7467
Fs_access.ensure_parent_directory_exists(outfile);
7568
let oc = Fs_access.open_file_for_writing(outfile);
76-
output_bytes(oc, contents);
69+
set_binary_mode_out(oc, true);
70+
Grain_formatting.Fmt.format(
71+
~write=output_string(oc),
72+
~source,
73+
~eol,
74+
program,
75+
);
7776
close_out(oc);
7877
| None =>
7978
set_binary_mode_out(stdout, true);
80-
print_bytes(contents);
79+
Grain_formatting.Fmt.format(~write=print_string, ~source, ~eol, program);
80+
flush(stdout);
8181
};
8282
};
8383

@@ -145,8 +145,8 @@ let enumerate_runs = opts =>
145145
let grainformat = runs => {
146146
List.iter(
147147
({input_path, output_path}) => {
148-
let (program, original_source, eol) = compile_parsed(input_path);
149-
try(format_code(~eol, ~output=?output_path, ~original_source, program)) {
148+
let (program, source, eol) = compile_parsed(input_path);
149+
try(format_code(~eol, ~output=?output_path, ~source, program)) {
150150
| exn =>
151151
Stdlib.Format.eprintf("@[%s@]@.", Printexc.to_string(exn));
152152
exit(2);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
open Grain_parsing;
2+
3+
// This structure isn't a tree at all, but we use a binary search algorithm to
4+
// efficiently find comments, which is tree-like in spirit.
5+
6+
type t = {
7+
comments: array(Parsetree.comment),
8+
line_map: Hashtbl.t(int, Parsetree.comment),
9+
};
10+
11+
let empty: t = {comments: [||], line_map: Hashtbl.create(0)};
12+
13+
let loc = cmt => {
14+
switch (cmt) {
15+
| Parsetree.Doc({cmt_loc})
16+
| Block({cmt_loc})
17+
| Line({cmt_loc})
18+
| Shebang({cmt_loc}) => cmt_loc
19+
};
20+
};
21+
22+
let from_comments = x => {
23+
// The array allows us to do a binary search for comments within a range.
24+
let comments = Array.of_list(x);
25+
// This map stores the last comment on a line, allowing us to quickly check
26+
// for formatter-ignore comments.
27+
let line_map = Hashtbl.create(Array.length(comments));
28+
List.iter(
29+
comment =>
30+
Hashtbl.add(line_map, loc(comment).loc_start.pos_lnum, comment),
31+
x,
32+
);
33+
{comments, line_map};
34+
};
35+
36+
let rec find_start_index = (array, point, ans, left, right) =>
37+
if (left <= right) {
38+
let middle = (left + right) / 2;
39+
if (loc(array[middle]).loc_start.pos_cnum >= point) {
40+
find_start_index(array, point, Some(middle), left, middle - 1);
41+
} else {
42+
find_start_index(array, point, ans, middle + 1, right);
43+
};
44+
} else {
45+
ans;
46+
};
47+
48+
let rec collect_range = (array, start, stop) =>
49+
if (start == Array.length(array)) {
50+
[];
51+
} else {
52+
let elem = array[start];
53+
if (loc(elem).loc_end.pos_cnum <= stop) {
54+
[elem, ...collect_range(array, start + 1, stop)];
55+
} else {
56+
[];
57+
};
58+
};
59+
60+
let query =
61+
(
62+
tree,
63+
{Location.loc_start: {pos_cnum: start}, loc_end: {pos_cnum: finish}},
64+
) => {
65+
let array = tree.comments;
66+
let start_index =
67+
find_start_index(array, start, None, 0, Array.length(array) - 1);
68+
switch (start_index) {
69+
| None => []
70+
| Some(start_index) => collect_range(array, start_index, finish)
71+
};
72+
};
73+
74+
let query_line = (tree, line) => Hashtbl.find_opt(tree.line_map, line);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
open Grain_parsing;
2+
3+
type t;
4+
5+
let empty: t;
6+
7+
let from_comments: list(Parsetree.comment) => t;
8+
9+
let query: (t, Location.t) => list(Parsetree.comment);
10+
let query_line: (t, int) => option(Parsetree.comment);

0 commit comments

Comments
 (0)