-
Notifications
You must be signed in to change notification settings - Fork 407
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a first draft with three main limitations: - it is language agnostic, so it does not know about field names - it is not able to parse comments - it does not break long lines The formatting rules are pretty simple: - lists composed only of atoms, quoted strings, templates, and singletons are displayed on a single line - other lists are displayed with a line break after each element - an empty line is inserted between toplevel stanzas The CLI is pretty light: it can either read a file or standard input, and fix a file in place. In addition, the command is named `unstable-fmt` for now, until some guarantees are given. Closes #940 Signed-off-by: Etienne Millon <me@emillon.org>
- Loading branch information
Showing
8 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
open! Import | ||
|
||
let parse_file path_opt = | ||
let fname, contents = | ||
match path_opt with | ||
| Some path -> | ||
Io.with_file_in path ~f:(fun ic -> | ||
let contents = Io.read_all ic in | ||
(Path.to_string path, contents) | ||
) | ||
| None -> | ||
let lines = Io.input_lines stdin in | ||
let contents = String.concat ~sep:"\n" lines in | ||
("<stdin>", contents) | ||
in | ||
Sexp.parse_string | ||
~fname | ||
~mode:Usexp.Parser.Mode.Many | ||
contents | ||
|
||
let can_be_displayed_inline = | ||
List.for_all ~f:(function | ||
| Usexp.Atom _ | ||
| Usexp.Quoted_string _ | ||
| Usexp.Template _ | ||
| Usexp.List [_] | ||
-> | ||
true | ||
| Usexp.List _ | ||
-> | ||
false | ||
) | ||
|
||
let pp_indent fmt indent = | ||
Format.pp_print_string fmt @@ String.make indent ' ' | ||
|
||
let print_inline_list fmt indent sexps = | ||
Format.fprintf fmt "%a(" pp_indent indent; | ||
let first = ref true in | ||
List.iter sexps ~f:(fun sexp -> | ||
if !first then | ||
first := false | ||
else | ||
Format.pp_print_string fmt " "; | ||
Usexp.pp Usexp.Dune fmt sexp | ||
); | ||
Format.pp_print_string fmt ")" | ||
|
||
let rec pp_sexp indent fmt = | ||
function | ||
( Usexp.Atom _ | ||
| Usexp.Quoted_string _ | ||
| Usexp.Template _ | ||
) as sexp | ||
-> | ||
Format.fprintf fmt "%a%a" | ||
pp_indent indent | ||
(Usexp.pp Usexp.Dune) sexp | ||
| Usexp.List sexps | ||
-> | ||
if can_be_displayed_inline sexps then | ||
print_inline_list fmt indent sexps | ||
else | ||
pp_sexp_list indent fmt sexps | ||
|
||
and pp_sexp_list indent fmt sexps = | ||
begin | ||
Format.fprintf fmt "%a(" pp_indent indent; | ||
let first = ref true in | ||
List.iter sexps ~f:(fun sexp -> | ||
let indent = | ||
if !first then | ||
begin | ||
first := false; | ||
0 | ||
end | ||
else | ||
indent + 1 | ||
in | ||
pp_sexp | ||
indent | ||
fmt | ||
sexp; | ||
Format.pp_print_string fmt "\n"; | ||
); | ||
Format.fprintf fmt "%a)" pp_indent indent; | ||
end | ||
|
||
let pp_top_sexp fmt sexp = | ||
Format.fprintf fmt "%a\n" (pp_sexp 0) sexp | ||
|
||
let pp_top_sexps fmt sexps = | ||
let first = ref true in | ||
List.iter sexps ~f:(fun sexp -> | ||
if !first then | ||
first := false | ||
else | ||
Format.pp_print_string fmt "\n"; | ||
pp_top_sexp fmt (Sexp.Ast.remove_locs sexp); | ||
) | ||
|
||
let with_output path_opt k = | ||
match path_opt with | ||
| None -> | ||
k Format.std_formatter | ||
| Some path -> | ||
Io.with_file_out ~binary:true path ~f:(fun oc -> | ||
k @@ Format.formatter_of_out_channel oc | ||
) | ||
|
||
let format_file ~input ~output = | ||
match parse_file input with | ||
| exception Usexp.Parse_error e -> | ||
Printf.printf | ||
"Parse error: %s\n" | ||
(Usexp.Parse_error.message e) | ||
| sexps -> | ||
with_output output (fun fmt -> | ||
pp_top_sexps fmt sexps; | ||
Format.pp_print_flush fmt () | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
open Import | ||
|
||
(** Reformat a dune file. [None] corresponds to stdin/stdout. *) | ||
val format_file : | ||
input:Path.t option -> | ||
output:Path.t option -> | ||
unit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
(a | ||
b) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
The empty list and atoms are printed as is: | ||
|
||
$ echo '()' | dune unstable-fmt | ||
() | ||
|
||
$ echo 'a' | dune unstable-fmt | ||
a | ||
|
||
Lists containing only atoms, quoted strings, templates, and singleton lists are | ||
printed inline: | ||
|
||
$ echo '(atom "string" %{template} (singleton))' | dune unstable-fmt | ||
(atom "string" %{template} (singleton)) | ||
|
||
Other lists are displayed one element per line: | ||
|
||
$ echo '(a (b c d) e)' | dune unstable-fmt | ||
(a | ||
(b c d) | ||
e | ||
) | ||
|
||
When there are several s-expressions, they are printed with an empty line | ||
between them: | ||
|
||
$ echo '(a b) (c d)' | dune unstable-fmt | ||
(a b) | ||
|
||
(c d) | ||
|
||
It is possible to pass a file name: | ||
|
||
$ dune unstable-fmt dune | ||
(a b) | ||
|
||
A file can be fixed in place: | ||
|
||
$ echo '(a (b c))' > dune_temp | ||
$ dune unstable-fmt --inplace dune_temp | ||
$ cat dune_temp | ||
(a | ||
(b c) | ||
) | ||
|
||
The --inplace flag requires a file name: | ||
|
||
$ dune unstable-fmt --inplace | ||
--inplace requires a file name | ||
[1] | ||
|
||
Parse errors are displayed: | ||
|
||
$ echo '(' | dune unstable-fmt | ||
Parse error: unclosed parenthesis at end of input | ||
|
||
and files are not removed when there is an error: | ||
|
||
$ echo '(a' > dune_temp | ||
$ dune unstable-fmt --inplace dune_temp | ||
Parse error: unclosed parenthesis at end of input | ||
$ cat dune_temp | ||
(a |