Skip to content

Conversation

@non-Jedi
Copy link
Member

@non-Jedi non-Jedi commented Jun 16, 2020

Closes julia-vscode/DocumentFormat.jl#70

JuliaFormatter was originally forked from DocumentFormat when @domluna
wanted to add canonicalising functionality. This resulted in a
large-scale refactoring that could not be immediately merged to
DocumentFormat (julia-vscode/DocumentFormat.jl#27).
Since then, extensive work has gone into JuliaFormatter. I believe it is
significantly more feature-rich than DocumentFormat at this time.

I believe there are significant advantages both to the Julia community
at large and to LanguageServer.jl to combine efforts on a single code
formatter.

Possible objections (I'm updating this list as discussions continue):

  • Missing features from DocumentFormat not present in JuliaFormatter.
    • I am not aware of any but this should be more thoroughly reviewed.
  • JuliaFormatter is more aggressive than desired (e.g. by inserting line breaks).
  • Lack of julia-vscode governance.
    • Taking a dependency on something not controlled by julia-vscode members may not be desired for reasons of making sure necessary features and fixes for vscode releases are merged in a timely manner.
  • JuliaFormatter has an additional dependency on DataStructures. My understanding is that this is undesirable for the way the vscode extension is packaged.
  • Stability concerns (crashing) with JuliaFormatter vs DocumentFormat

@non-Jedi non-Jedi requested a review from davidanthoff June 16, 2020 14:10
@domluna
Copy link

domluna commented Jun 16, 2020 via email

@non-Jedi
Copy link
Member Author

non-Jedi commented Jun 16, 2020

Thanks for chiming in @domluna, if you could point out which methods would need to be defined for a custom style in order to prevent inserting line breaks (since that was a point of concern in julia-vscode/DocumentFormat.jl#70), that would probably be helpful for this discussion. Or if it's more or less all of them, that's also valuable information. Is it as simple as defining nest_if_over_margin! to be a no-op?

@non-Jedi non-Jedi added this to the Triage milestone Jun 16, 2020
@pfitzseb
Copy link
Member

I'm in favour of this.

David has some concerns about this though. AFAICT JuliaFormatter is more widely used and more powerful than DocumentFormat, so imho it's a good idea regardless.

@non-Jedi
Copy link
Member Author

non-Jedi commented Jun 16, 2020

I've added that concern to the OP.

My opinion is that even if there are regressions due to this change, JuliaFormatter provides enough additional features that making the change is worthwhile. Just in the past couple hours of using this branch, I've found myself actually making use of the formatting feature--which I had previously largely ignored--because it's so much better. As a practical example of the improvement, JuliaFormatter correctly formats #739 which is currently extremely difficult to read.

I believe getting DocumentFormat to have the same level of features as JuliaFormatter would introduce more bugs than currently exist in JuliaFormatter. If minimizing the absolute number of bugs in the codebase is the end goal, making this switch is counterproductive (as is introducing features to DocumentFormat), but if maximizing the utility to end-users is the goal, using JuliaFormatter is a better path to improving formatting than incrementally improving DocumentFormat.

Comment on lines 1 to 16
const default_format_options = (4, 100)

struct FormatOptions <: JuliaFormatter.AbstractStyle
indent::Int
margin::Int
end
FormatOptions() = FormatOptions(default_format_options...)

FormatOptions(options::Vararg{Any,length(default_format_options)}) =
FormatOptions(something.(options, default_format_options)...)

JuliaFormatter.getstyle(x::FormatOptions) = x

# All functions that don't have a dispatch defined for FormatOptions
# fallback to the definition for JuliaFormatter.DefaultStyle. This is
# how we customize the behavior of the formatter.
Copy link
Member

@aviatesk aviatesk Jun 16, 2020

Choose a reason for hiding this comment

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

I would like to first make sure whether we want to go with JuliaFormatter.jl before giving actual review, but here I also want to comment a bit about configurations here:
Actually with the .JuliaFormatter.toml configuration file, we don't need to handle specific formatting options by ourselves -- all what we need to do is to search for a configuration file and pass those configurations there like Juno does.
Then we get free from tasks to sync these frontend formatting option specs with actual formatter options anymore (in other word we won't need something like this PR.
So I think there won't be any overhead of syncing the development in frontend (i.e. julia-vscode side) and that of JuliaFormatter.jl once we setup the configuration handlings.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, a config file is also how other prominent linters (e.g. ESLint) work and much more sensible than putting a bunch of options into every editor's UI.

Copy link
Member Author

@non-Jedi non-Jedi Jun 16, 2020

Choose a reason for hiding this comment

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

I think it's still valuable to provide configuration at the editor level so that formatting files always works as the user expects without having to create a .JuliaFormatter.toml file for one-off scripts (if e.g. the user prefers 2-space tabs as used in the DifferentialEquations codebase). Ideally the editor should have a single option that sets this kind of stuff both for the editor's builtin indentation engine and the formatter. I agree that we should hook this up so that a .JuliaFormatter.toml file overrides language server configuration.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but imho that should only happen for "common" options like indentation (i.e. options that are shared by all languages).

Copy link
Member

Choose a reason for hiding this comment

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

How about this: we make the VS Code config settings scope application, so they can only be configured in user settings, but not as workspace or folder settings. I think what we should try to avoid is to have two different ways to configure folder specific settings, and I think the separate .JuliaFormatter.toml story then makes more sense than a VS Code specific configuration setting with window scope.

I think in terms of implementations this would mean that a .JuliaFormatter.toml would always take precedence (if it exists) over any config settings that the LS receives from the client, right?

What is a lot less clear to me is which config settings we should have in the client... Minimally I guess the option to specify a style? But I don't really see a good reason not to allow all the other settings to be specified as well...

@domluna
Copy link

domluna commented Jun 16, 2020

Thanks for chiming in @domluna, if you could point out which methods would need to be defined for a custom style in order to prevent inserting line breaks (since that was a point of concern in julia-vscode/DocumentFormat.jl#70), that would probably be helpful for this discussion. Or if it's more or less all of them, that's also valuable information. Is it as simple as defining nest_if_over_margin! to be a no-op?

Sort of, the main thing is to make check whether the text is on the same line in the source file and set join_lines based on that.

https://github.com/domluna/JuliaFormatter.jl/blob/master/src/styles/yas.jl#L72

For example, in the above function, for tuples, everything is put on the same line. Which is why join_lines is set to true.

Also don't insert placeholder nodes, if those aren't there the nesting stage is a noop.

Of course there should be still be indentation of some sort, the YAS style indentation seems to be the closest to DocumentFormat but that could be explored.

I think it should be fairly straightforward to implement.

@domluna
Copy link

domluna commented Jul 2, 2020

I have a WIP DocumentFormat-esque style domluna/JuliaFormatter.jl#256

@ZacLN
Copy link
Contributor

ZacLN commented Jul 2, 2020

Thanks @non-Jedi for the really helpful issue. Broadly I'm in support of this, going through your list:

(I'll add I've not actually used JF so please forgive any misconceptions)

  1. Missing features. I assume JF is far ahead DF as is. My only question - Is JF currently able to apply formatting on the fly (i.e. on-keystroke) or is it structured so that this would be possible (and performant)? Leaving that option open was (I think) one of the drivers when designing DF.
  2. This is my only concern. I don't like aggressive formatting, other do - there need to be options. I'd also suggest that they should fit into the vscode configuration story (including the option within vscode to point to whatever format of config file JF uses).
  3. Not concernd about this as long as 2. and 5. are resolved in a satisfactory manner. I minimally maintain DF and really don't have much interest in it.
  4. I don't have as a strong feeling on this as others but wouldn't seem to be a deal-breaker to me.
  5. I agree with the points made by David on the other thread, but as a risk it would seem to be manageable. A bit dependent on whether @domluna is interested/able to commit a little bit of time to it.

@domluna
Copy link

domluna commented Jul 2, 2020

My only question - Is JF currently able to apply formatting on the fly (i.e. on-keystroke) or is it structured so that this would be possible (and performant)? Leaving that option open was (I think) one of the drivers when designing DF.

The most common use of JF is formatting the entire file on save. You can pass arbitrary julia code and it'll format it but without the context of the entire file this might not be ideal. However, this might be sufficient for this use case but I'm not familiar with the API requirements.

Timings formatting for a julia file read into REPL s:

julia> @btime DocumentFormat.format($s);
  18.235 ms (54518 allocations: 4.20 MiB)

julia> @btime JuliaFormatter.format_text($s);
  3.088 ms (47567 allocations: 2.85 MiB)

Timings for a small piece of text, I imagine this would be a touch type use case (?)

julia> str = "foo(argument1,                           argument2,                                                    argument3            )"
"foo(argument1,                           argument2,                                                    argument3            )"

julia> @btime DocumentFormat.format($str);
  74.314 μs (256 allocations: 15.69 KiB)

julia> @btime JuliaFormatter.format_text($str);
  8.825 μs (230 allocations: 16.06 KiB)

using DocumentFormat v3.1.0

@ZacLN
Copy link
Contributor

ZacLN commented Jul 2, 2020

That looks good (in terms of being much faster) - I was kind of asking whether it is structured so that, for example, given some small node of a file's CST you could quickly get the context necessary to format that node without also formatting the whole file

@EricForgy
Copy link

I've never used JuliaFormatter, but this PR makes sense to me. I came across it because I wanted no spaces with TypeVar definitions.

Lack of julia-vscode governance.

Why not just move JuliaFormatter to an org? Maybe here?

@domluna
Copy link

domluna commented Jul 2, 2020

That looks good (in terms of being much faster) - I was kind of asking whether it is structured so that, for example, given some small node of a file's CST you could quickly get the context necessary to format that node without also formatting the whole file

Partial formatting would probably be what you're looking for. This would still operate on the entire file (to figure out indentation and what not) but the output would only differ for the lines specified. So the lines that you don't want formatted would essentially have formatting disabled https://domluna.github.io/JuliaFormatter.jl/stable/skipping_formatting/

@singularitti
Copy link

singularitti commented Jul 3, 2020

Don't want to diverge here but I would love to see VSCode uses JF. I currently maintain Julia Formatter and it has a major issue: speed. The reason is I need to start and stop a Julia process for each file to be formatted. This formatter is the only one I have used to be waiting for seconds until it's done. It would be faster, I think, if you have a running Julia process all the time and have JF precompiled.

@aviatesk
Copy link
Member

aviatesk commented Jul 3, 2020

That looks good (in terms of being much faster) - I was kind of asking whether it is structured so that, for example, given some small node of a file's CST you could quickly get the context necessary to format that node without also formatting the whole file

AFAICT, JuliaFormatter works as far as an input text represents a valid Julia expressions and is parsable by CSTParser (am I right, @domluna ?).
So as an editor integration, we can support "selection-based formatting", which I think it covers @ZacLN 's concern (as a result, Juno supports 1.) whole document formatting 2.) selection-based formatting, and 3.) on-save (whole-document) formatting).

Another note, I found DocumentFormat works even if a given text doesn't represent a valid Julia expression, and I guess this is the main difference in term of how formatting works in both pkgs.

@domluna
Copy link

domluna commented Jul 3, 2020

AFAICT, JuliaFormatter works as far as an input text represents a valid Julia expressions and is parsable by CSTParser (am I right, @domluna ?)

yeah that's right

@non-Jedi
Copy link
Member Author

non-Jedi commented Jul 6, 2020

@ZacLN for mitigating point 5, would it be desirable to have usage of JuliaFormatter be opt-in via a configuration option for a release or two? Since DocumentFormat is really only called in one place, this wouldn't be difficult to accomplish.

@jlperla
Copy link

jlperla commented Aug 7, 2020

Not to be "that guy" who bumps PRs without contributing code, but...

Any hope on getting these sorts of features into vscode /either directly in the existing formatter or adopting this one?

@davidanthoff
Copy link
Member

I think the main blocker is that a) we're still not done with the major other work happening in LS land, and I'd rather have us finish us those parts first before we start a new big change, but more generally b) that no one had time for much lately :) But maybe we should just say that after Juliacon we make this here a priority.

@pfitzseb
Copy link
Member

JuliaFormatter will also currently bring in 7 new dependencies, which isn't too great (good news is that there don't appear to be any binary dependencies, so distribution should just work).

@pfitzseb
Copy link
Member

pfitzseb commented Aug 2, 2021

Ok, I have no idea what GitHub (or I) did with the commit history here. Will close this in favor of #972.

@pfitzseb pfitzseb closed this Aug 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integration/merge with JuliaFormatter?