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

Automatic formatting of Inko code #334

Closed
yorickpeterse opened this issue Sep 13, 2022 · 4 comments
Closed

Automatic formatting of Inko code #334

yorickpeterse opened this issue Sep 13, 2022 · 4 comments
Assignees
Labels
compiler Changes related to the compiler feature New things to add to Inko, such as a new standard library module
Milestone

Comments

@yorickpeterse
Copy link
Collaborator

Inko should provide an official code formatter (i.e. inko format). This formatter should be opinionated and not offer any settings to configure.

The formatter won't preserve custom whitespace outside of comments and strings, instead always formatting files according to its own style. This makes things a bit easier as preserving custom whitespace is difficult.

The formatter likely has to operate at the lexer level. If we operate at the parser level we have to first change the parser such that we can preserve comments.

For linting purposes the formatter should have two modes: format a bunch of files (or all of them in the project), or simply check if they would be changed by formatting. The latter could be used in a CI environment to ensure changed code is formatted accordingly.

@yorickpeterse yorickpeterse added feature New things to add to Inko, such as a new standard library module and removed type::feature labels Mar 15, 2023
@yorickpeterse yorickpeterse removed this from the 0.2.5 milestone Mar 15, 2023
@yorickpeterse yorickpeterse added this to the 0.15.0 milestone Jun 6, 2023
@yorickpeterse yorickpeterse self-assigned this Jun 6, 2023
@spartanatreyu
Copy link

Please do these two things.

  1. Allow indentation to be configurable between tabs, or a set number of spaces.
  2. Set the sensible default of tabs. Why:
    • Spaces have accessibility concerns that tabs do not suffer from.
      • This alone is reason enough to have tabs as default.
    • Older languages made in previous decades DID have technical reasons to choose spaces over tabs, but those reasons no longer hold true today.
    • Newer projects that start off with space-based indentation end up making the concession that tabs make more sense but it's too late to change anything now (e.g. rustfmt, standard, etc). Recently, ESLint has taken the extra step to remove whitespace formatting from it's linter.
    • Tabs allow for more forward-thinking features like elastic tabstops and proportional fonts.
    • Spaces are nonparameterizable while tabs are customised to the user's aesthetic pallet. This is the least useful and probably the most bike-shedded point, but it is an added nicety.

@yorickpeterse
Copy link
Collaborator Author

@spartanatreyu My plan is to do the exact opposite 😄:

No configuration settings whatsoever (e.g. like gofmt), and use 2 spaces for indentation like we do now. The moment you introduce any settings, the value of an automatic formatter begins to diminish.

Spaces have accessibility concerns that tabs do not suffer from.

Are you referring to screen readers? As far as I know, such setups are perfectly capable of handling space based indentation, albeit through configuration, which one likely needs to do anyway, because having it read "curly open brace" out loud every time is too time consuming.

Tabs allow for more forward-thinking features like elastic tabstops and proportional fonts.

Neither of which are supported by any of the well known editing environments as far as I know, and the use of proportional fonts is such a niche I'm not sure it's even worth considering (and again, no widespread support in the first place).

Spaces are nonparameterizable while tabs are customised to the user's aesthetic pallet. This is the least useful and probably the most bike-shedded point, but it is an added nicety.

That's the whole point of using spaces though: to ensure that no matter what tool you use to view the code, it looks consistent. This combined with it being more difficult to mix spaces and tabs by accident (because if spaces are used, editors typically map Tab to the appropriate amount of spaces), makes it much easier to get started as you don't need to fiddle with your indentation settings.

@spartanatreyu
Copy link

Hopefully we can agree on this single point:

  • Different users have different needs.

If not, I think the focus needs to be on that point first.

If you do, I have some responses, and some questions:

Responses to points:

The moment you introduce any settings, the value of an automatic formatter begins to diminish.

  • This is really more of an "It depends"...
  • Adding something like an .editorconfig file to a project that already has 10 other config files is bad. There's discussions between some projects about moving all of these files into a .well-known directory but I think that's more treating the symptoms rather than the causes.
  • However, some languages have a well defined config file that exist for every project because it's the best solution. For example: setting a rust project's edition inside a Cargo.toml file, build/release settings, Linter settings (e.g. warn/error on deprecated functions), etc...

elastic tabstops and proportional fonts [...] no widespread support in the first place.

  • This is a chicken and egg thing, it doesn't matter until someone big does it good enough. No one was serious about official language IDE extensions until Microsoft pushed out LSPs.

That's the whole point of using spaces though: to ensure that no matter what tool you use to view the code, it looks consistent.

  • I would argue that there's something passed over here. There is a difference between something being consistent between computers, and something being consistent between developers.
  • Alice and Bob may be looking at the same code, but one may be coding with word wrapping, and another may use a different theme. They can both make changes to the code and agree on what changes have been made while the same code can look different to appear more readable for each developer.
  • If the point is to make the code look consistent, then that would mean the language mandating an editor and a theme used. But doing this wouldn't help Bob who has astigmatism and who needs to change the theme and font size to be able to code.
  • Consistency only matters when it comes to the data itself, not the appearance of said data.

Questions about points:

My plan is to do the exact opposite [...] and use 2 spaces for indentation like we do now.

  • Question: How do you plan to accommodate devs who can't read 2 space based indentation?
  • Question: How do you intend to resolve the 3 space problem? (i.e. How would the formatter decide which indentation level to place a block of code that has an odd number of spaces before it.)

@yorickpeterse
Copy link
Collaborator Author

@spartanatreyu The goal of Inko's code formatter is to enforce a single style across all Inko projects. Enforcing the style preferences of a specific project is specifically a non-goal, and never will. This is what Go did right and rustfmt in my opinion did wrong (besides choosing 120 as a line length limit).

Ensuring there's only a single style across all projects makes jumping between projects much easier, and removes the whole debate on how certain things should be formatted, what indentation should be used, etc.

This is really more of an "It depends"...

Not at all. If you introduce a setting to change the formatting rules, some projects will start using it. When that happens, you no longer have a consistent formatting style across projects. This gets worse as the number of settings and projects increases, as the number of combinations of the two increases.

Adding something like an .editorconfig file to a project that already has 10 other config files is bad. There's discussions between some projects about moving all of these files into a .well-known directory but I think that's more treating the symptoms rather than the causes.

Which is exactly why there won't be a configuration file 😃

This is a chicken and egg thing, it doesn't matter until someone big does it good enough.

Perhaps, but it's still not relevant to Inko. I have no interest in elastic tab stops myself, and even if Inko were to use it and get popular, editors simply aren't going to bother. Even if they did, elastic tab stops don't offer any benefits over just using spaces and not fuzzing about the few bytes per file you might save from using tabs.

To put it differently, arguing over tabs versus spaces is very much a case of bike shedding. I don't want such a debate to keep taking place, so Inko's formatter will only support spaces for indentation.

I would argue that there's something passed over here. [...]

I'm honestly not sure what argument you're trying to make here. The "If the point is to make the code look consistent, then that would mean the language mandating an editor and a theme used." bit is also a slippery slope fallacy that isn't useful, because following that same train of thought we should just ship everybody the exact same computer.

The goal of a formatter with an ironclad set of rules it enforces is to ensure that the formatting is consistent everywhere, not how it's presented in terms of colors, font sizes, etc. Some users may disagree with this, but you'll always find such people and I'm not going to cater towards a tiny vocal minority

With that all said: I very much appreciate the feedback and thoughts, but the plan for Inko's formatter remains the same: no configuration, and a single style it enforces, and spaces for indentation. Of course certain aspects of that formatting may change based on feedback (e.g. how long lines are wrapped), but only if there's actual examples/use cases/etc, not artificial cases people come up with to further their own preferences.

yorickpeterse added a commit that referenced this issue Feb 12, 2024
yorickpeterse added a commit that referenced this issue Feb 13, 2024
yorickpeterse added a commit that referenced this issue Feb 13, 2024
yorickpeterse added a commit that referenced this issue Feb 13, 2024
yorickpeterse added a commit that referenced this issue Feb 13, 2024
yorickpeterse added a commit that referenced this issue Feb 15, 2024
yorickpeterse added a commit that referenced this issue Feb 16, 2024
yorickpeterse added a commit that referenced this issue Feb 17, 2024
yorickpeterse added a commit that referenced this issue Feb 22, 2024
yorickpeterse added a commit that referenced this issue Feb 22, 2024
yorickpeterse added a commit that referenced this issue Feb 26, 2024
yorickpeterse added a commit that referenced this issue Feb 26, 2024
yorickpeterse added a commit that referenced this issue Feb 26, 2024
yorickpeterse added a commit that referenced this issue Feb 27, 2024
yorickpeterse added a commit that referenced this issue Feb 27, 2024
yorickpeterse added a commit that referenced this issue Feb 28, 2024
yorickpeterse added a commit that referenced this issue Feb 28, 2024
yorickpeterse added a commit that referenced this issue Feb 28, 2024
yorickpeterse added a commit that referenced this issue Feb 29, 2024
yorickpeterse added a commit that referenced this issue Feb 29, 2024
yorickpeterse added a commit that referenced this issue Feb 29, 2024
yorickpeterse added a commit that referenced this issue Mar 9, 2024
yorickpeterse added a commit that referenced this issue Mar 10, 2024
yorickpeterse added a commit that referenced this issue Mar 11, 2024
yorickpeterse added a commit that referenced this issue Mar 12, 2024
yorickpeterse added a commit that referenced this issue Mar 12, 2024
yorickpeterse added a commit that referenced this issue Mar 13, 2024
yorickpeterse added a commit that referenced this issue Mar 13, 2024
yorickpeterse added a commit that referenced this issue Mar 13, 2024
yorickpeterse added a commit that referenced this issue Mar 14, 2024
yorickpeterse added a commit that referenced this issue Mar 14, 2024
yorickpeterse added a commit that referenced this issue Mar 15, 2024
yorickpeterse added a commit that referenced this issue Mar 15, 2024
yorickpeterse added a commit that referenced this issue Mar 15, 2024
yorickpeterse added a commit that referenced this issue Mar 16, 2024
yorickpeterse added a commit that referenced this issue Mar 16, 2024
yorickpeterse added a commit that referenced this issue Mar 16, 2024
yorickpeterse added a commit that referenced this issue Mar 19, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. This change makes
formatting string literals a lot easier, and solves the debate of
"should I use single or double quoted?".

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 20, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 20, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 22, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 22, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 22, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 23, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 23, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 24, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 25, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 25, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
yorickpeterse added a commit that referenced this issue Mar 25, 2024
This add the command `inko fmt`, used for formatting (or checking the
formatting of) source files. The command supports formatting both
existing files as well as input read from STDIN.

Style wise, the way closures are formatted is changed: closure arguments
are always passed inside the parentheses, with the purpose of removing
the trailing closure syntax in the release _after_ the one that includes
`inko fmt`. This means that instead of this:

    foo.bar(10, 20) fn { ... }.baz(30)

The code is formatted like so:

    foo.bar(10, 20, fn { ... }).baz(30)

The purpose of this change is to make the syntax more familiar, and to
make both parsing and formatting less complicated. Parsing in particular
is made more difficult by the presence of trailing closures.

This commit also includes changes to string literals: both single and
double quoted strings are now semantically the same, meaning they both
support escape sequences and string interpolation. String interpolation
syntax is also changed from this:

    let name = 'Alice'

    "hello {name}"

To this:

    let name = 'Alice'

    "hello ${name}"

These changes make the syntax more consistent and less ambiguous, and as
a result make formatting the code less of a challenge.

This fixes #334.

Changelog: added
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler Changes related to the compiler feature New things to add to Inko, such as a new standard library module
Projects
No open projects
Status: Done
Development

No branches or pull requests

2 participants