Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: experiment with line directives #2426

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lib/Cmt.ml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ module T = struct

let is_docstring = function Comment _ -> false | Docstring _ -> true

let is_line_directive = function
| Docstring _ -> None
| Comment {txt; _} -> (
try
let pattern = "<LINE_DIRECTIVE:([0-9]+)>(.*)" in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to encode this as a string, we could have a new constructor.

let regex = Re.Posix.compile_pat pattern in
match Re.exec_opt regex txt with
| Some groups ->
let line_number = Int.of_string (Re.Group.get groups 1) in
let name = Re.Group.get groups 2 in
Some (line_number, name)
| None -> None
with _ -> None )

let compare = Poly.compare

let sexp_of_t cmt =
Expand Down
2 changes: 2 additions & 0 deletions lib/Cmt.mli
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ val create_docstring : string -> Location.t -> t

val is_docstring : t -> bool

val is_line_directive : t -> (int * string) option

val loc : t -> Location.t

val txt : t -> string
Expand Down
31 changes: 23 additions & 8 deletions lib/Cmts.ml
Original file line number Diff line number Diff line change
Expand Up @@ -449,19 +449,23 @@ let find_cmts ?(filter = Fn.const true) t pos loc =
picked )

let break_comment_group source margin a b =
let a = Cmt.loc a and b = Cmt.loc b in
let loc_a = Cmt.loc a and loc_b = Cmt.loc b in
let vertical_align =
Location.line_difference a b = 1 && Location.compare_start_col a b = 0
Location.line_difference loc_a loc_b = 1
&& Location.compare_start_col loc_a loc_b = 0
in
let horizontal_align =
Location.line_difference a b = 0
Location.line_difference loc_a loc_b = 0
&& List.is_empty
(Source.tokens_between source a.loc_end b.loc_start
(Source.tokens_between source loc_a.loc_end loc_b.loc_start
~filter:(function _ -> true) )
in
not
( (Location.is_single_line a margin && Location.is_single_line b margin)
&& (vertical_align || horizontal_align) )
Option.is_some (Cmt.is_line_directive a)
|| Option.is_some (Cmt.is_line_directive b)
|| not
( ( Location.is_single_line loc_a margin
&& Location.is_single_line loc_b margin )
&& (vertical_align || horizontal_align) )

module Asterisk_prefixed = struct
let split txt {Location.loc_start; _} =
Expand Down Expand Up @@ -633,6 +637,13 @@ let fmt_cmt (conf : Conf.t) cmt ~fmt_code pos =
| `Unwrapped (x, _) -> Unwrapped.fmt ~opn ~offset x
| `Asterisk_prefixed x -> Asterisk_prefixed.fmt ~opn x

let fmt_cmt (conf : Conf.t) cmt ~fmt_code pos =
match Cmt.is_line_directive cmt with
| Some (line, filename) ->
let open Fmt in
str (Format.sprintf "#%i %S" line filename)
| None -> fmt_cmt conf cmt ~fmt_code pos

let fmt_cmts_aux t (conf : Conf.t) cmts ~fmt_code pos =
let open Fmt in
let groups =
Expand Down Expand Up @@ -670,7 +681,11 @@ let fmt_cmts t conf ~fmt_code ?pro ?epi ?(eol = Fmt.fmt "@\n") ?(adj = eol)
let adj_cmt = eol_cmt && Location.line_difference last_loc loc = 1 in
fmt_or_k eol_cmt (fmt_or_k adj_cmt adj eol) (fmt_opt epi)
in
fmt_opt pro $ fmt_cmts_aux t conf cmts ~fmt_code pos $ epi
( match List.hd cmts with
| Some cmt when Option.is_some @@ Cmt.is_line_directive cmt -> fmt "\n"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this doesn't work if the line directive follows a break. (as seen in the tests)

We would need a new formatting primitive to remove the indentation that was added by the previous break: fmt "@\n" $ remove_indentation. Though, I'm not sure that's possible with Format's current implementation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few things like setting max indent to zero but didn't find any fix to this issue.

| _ -> fmt_opt pro )
$ fmt_cmts_aux t conf cmts ~fmt_code pos
$ epi

let fmt_before t conf ~fmt_code ?pro ?(epi = Fmt.break 1 0) ?eol ?adj loc =
fmt_cmts t conf (find_cmts t `Before loc) ~fmt_code ?pro ~epi ?eol ?adj loc
Expand Down
1 change: 1 addition & 0 deletions lib/dune
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
(backend bisect_ppx))
(libraries
format_
re
ocaml_common
parser_standard
parser_extended
Expand Down
3 changes: 1 addition & 2 deletions test/passing/dune.inc
Original file line number Diff line number Diff line change
Expand Up @@ -3506,8 +3506,7 @@
(action
(with-stdout-to line_directives.ml.stdout
(with-stderr-to line_directives.ml.stderr
(with-accepted-exit-codes 1
(run %{bin:ocamlformat} --margin-check %{dep:tests/line_directives.ml}))))))
(run %{bin:ocamlformat} --margin-check %{dep:tests/line_directives.ml})))))

(rule
(alias runtest)
Expand Down
5 changes: 0 additions & 5 deletions test/passing/tests/line_directives.ml.err
Original file line number Diff line number Diff line change
@@ -1,5 +0,0 @@
ocamlformat: ignoring "tests/line_directives.ml" (syntax error)
File "tests/line_directives.ml", line 1, characters 1-9:
1 | #3 "f.ml"
^^^^^^^^
Error: Invalid lexer directive "#3 \"f.ml\"": line directives are not supported
7 changes: 7 additions & 0 deletions test/passing/tests/line_directives.ml.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#3 "f.ml"

let x =

#4 "f.ml"
y
#5 "f.ml"
Empty file.
11 changes: 6 additions & 5 deletions vendor/parser-extended/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -617,13 +617,14 @@ rule token = parse
{ error lexbuf (Illegal_character illegal_char) }

and directive = parse
| ([' ' '\t']* (['0'-'9']+ as _num) [' ' '\t']*
("\"" ([^ '\010' '\013' '\"' ] * as _name) "\"") as directive)
| ([' ' '\t']* (['0'-'9']+ as line) [' ' '\t']*
("\"" ([^ '\010' '\013' '\"' ] * as name) "\""))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might accept #if, etc.. from cppo, though that wouldn't format cppo files in most cases.

We could also accept any line starting with # by redesigning the parsing of toplevel directive.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think cppo directives can be handled in the same way. I was thinking that we can encode the conditions as regular if expressions with an attribute indicating they are cppo conditions, but didn't give it more thought than that.

[^ '\010' '\013'] *
{
(* Line directives are not preserved by the lexer so we error out. *)
let explanation = "line directives are not supported" in
error lexbuf (Invalid_directive ("#" ^ directive, Some explanation))
let loc = Location.curr lexbuf in
let line = int_of_string line in
let s = Format.sprintf "<LINE_DIRECTIVE:%i>%s" line name in
COMMENT (s, loc)
}
and comment = parse
"(*"
Expand Down
11 changes: 6 additions & 5 deletions vendor/parser-standard/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -616,13 +616,14 @@ rule token = parse
{ error lexbuf (Illegal_character illegal_char) }

and directive = parse
| ([' ' '\t']* (['0'-'9']+ as _num) [' ' '\t']*
("\"" ([^ '\010' '\013' '\"' ] * as _name) "\"") as directive)
| ([' ' '\t']* (['0'-'9']+ as line) [' ' '\t']*
("\"" ([^ '\010' '\013' '\"' ] * as name) "\""))
[^ '\010' '\013'] *
{
(* Line directives are not preserved by the lexer so we error out. *)
let explanation = "line directives are not supported" in
error lexbuf (Invalid_directive ("#" ^ directive, Some explanation))
let loc = Location.curr lexbuf in
let line = int_of_string line in
let s = Format.sprintf "<LINE_DIRECTIVE:%i>%s" line name in
COMMENT (s, loc)
}
and comment = parse
"(*"
Expand Down
Loading