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

Conversation

gpetiot
Copy link
Collaborator

@gpetiot gpetiot commented Sep 6, 2023

No description provided.

Copy link
Collaborator

@Julow Julow left a comment

Choose a reason for hiding this comment

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

I think that's a good idea :) Though, the formatting is not reliable due to the current implementation of Fmt.

I have no solution for the newline problem.

| 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.

@@ -684,7 +695,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.

| ([' ' '\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.

@hhugo
Copy link
Collaborator

hhugo commented Oct 31, 2023

Can you explain the expected workflow for formatting files containing line directive ? It's weird to try to preserve location with line directive and also shuffle location while formatting.

@gpetiot
Copy link
Collaborator Author

gpetiot commented Nov 1, 2023

Can you explain the expected workflow for formatting files containing line directive ? It's weird to try to preserve location with line directive and also shuffle location while formatting.

It gives the users the choice to format or not their file, they can still list their files in .ocamlformat-ignore. It makes sense to me that users would want to format a file containing a line directive and then manually edit the line directive if they wish to get something closer to the new shape of the file.

@hhugo
Copy link
Collaborator

hhugo commented Nov 1, 2023

Can you explain the expected workflow for formatting files containing line directive ? It's weird to try to preserve location with line directive and also shuffle location while formatting.

It gives the users the choice to format or not their file, they can still list their files in .ocamlformat-ignore. It makes sense to me that users would want to format a file containing a line directive and then manually edit the line directive if they wish to get something closer to the new shape of the file.

The only workflow I know for line directive is inside automatically generated files in which case the source files are not meant to be edited directly. Changing the shape of a file with line directive means that locations updated by the line directive are now incorrect and useless.

@panglesd
Copy link
Contributor

panglesd commented Nov 2, 2023

I had a vague idea on how to use that to format cppo-processed files, at least some of them:

  • cppo-process them to get a file with line-directives
  • ocamlformat this file with line directives
  • Use the line directives to reconstruct a formatted original file. Something along the lines of:
    • Separate each file in blocks separated by directives (#if, #else, ...)
    • Match the blocks using the line directives: if two block start at the same line they match
    • Replace the unformatted blocks by formatted one.

This is just the beginning of an idea and that might be a bad one, there might be simpler ways to format cppo files, but that's what triggered my interest in formatting line directives (which, compared to cppo directives such as #if, are at least valid OCaml).

@hhugo
Copy link
Collaborator

hhugo commented Nov 2, 2023

I had a vague idea on how to use that to format cppo-processed files, at least some of them:

  • cppo-process them to get a file with line-directives

  • ocamlformat this file with line directives

  • Use the line directives to reconstruct a formatted original file. Something along the lines of:

    • Separate each file in blocks separated by directives (#if, #else, ...)
    • Match the blocks using the line directives: if two block start at the same line they match
    • Replace the unformatted blocks by formatted one.

This is just the beginning of an idea and that might be a bad one, there might be simpler ways to format cppo files, but that's what triggered my interest in formatting line directives (which, compared to cppo directives such as #if, are at least valid OCaml).

This seems to be a very convoluted workflow that gives many opportunities for failures.
There exists an alternative to cppo which works fine with ocamlformat already (and maybe merlin as well to some degree).
See https://github.com/janestreet/ppx_optcomp
It's not as powerful as cppo but could be extended to fit the need.

I use a custom ppx in js_of_ocaml that uses the same principals.
See https://github.com/ocsigen/js_of_ocaml/blob/master/compiler/ppx/ppx_optcomp_light.ml

I suspect efforts would be better spent extending (or replacing) ppx_optcomp.

@gpetiot gpetiot closed this Nov 27, 2023
@gpetiot gpetiot deleted the line-directives branch November 27, 2023 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants