helix: Formatter doesn't have access to filename

#2942 added formatter option, which is very useful. I used it prettier but quickly discovered that I have conflicting formatting between CI and my editor.

This happens because prettier change formatting based on filename, e.g. package.json formatted with json-stringify but other *.json files formatted with json parser. https://github.com/prettier/prettier/blob/2316e2fecd171335fc00f3ec97b7e46cc8964153/src/language-js/index.js#L81-L94

To solve this use case, prettier provides –stdin-filepath, but I can’t use it in helix since there is no way to pass the filename into fomatter option.

Having a placeholder of some kind would be great, e.g. $filename.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 23
  • Comments: 23 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Previously #2942 did implement passing a file path to the formatter, but it was removed since stdin was deemed sufficient. Maybe this use case will change things.

You can manually specify the parser for prettier to use to avoid needing the file name, see https://prettier.io/docs/en/options.html#parser.

E.g.:

[[language]]
name = "typescript"
auto-format = true
formatter = { command = "prettier", args = ["--parser", "typescript"] }

Just got mugged…

@zjr It wasn’t my intention. I wanted to contribute by giving a feedback as a user and to highlight the importance of this feature for formatters usually based on local config files. Nothing else.

And if I had enough time and if I knew rust well, it would have been with pleasure. Could you please give the benefit of doubt next time and not make hasty conclusions. It’s toxical for the community. Although English is not main language of many contributers and they can miss some nuances.

To address the original issue, it is possible to create a custom language block for a specific set of files. This can be done by adding the following block to your languages.toml file:

[[language]]
name = "package.json" # this can be set to any string that doesn't conflict with other language names
scope = "source.json"
roots = ["package.json"]
file-types = ["package.json", "package-lock.json", "composer.json"]
formatter = { command = "npx", args = ["prettier", "--stdin-filepath", "package.json"] }
language-servers = ["vscode-json-language-server"]
auto-format = true
grammar = "json"

To get syntax highlighting to work, you also need to copy (or symlink) the runtime/queries/json directory to runtime/queries/package.json.

This seems like the cleanest workaround for now (to me), but I agree that it would be best if helix supported passing the filename to the formatter.

I’m using this as a workaround.

formatter = { command = "sh", args = ["-c", """
  file=$(
    ps -p "${PPID}" -o "command=" \
      | awk '{ print $NF }'
  )
  exec prettier \
    --stdin-filepath="${file}" \
    --config-precedence=file-override
"""] }

I’ve encountered two cases where this would be useful:

  • clang-format-diff needs to know what file you’re editing, so it can apply a relevant section of the diff. This is useful when you want to format new changes in a codebase that is not fully formatted.
  • ~goimports requires a filename, not stdin. I think this is because it needs to know the name of the module when sorting imports.~ I’m wrong, goimports works over stdin.

clang-format would also benefit from being able to pass the filename.

@Anthuang this is hard to use, because the cli programmer know better than you. for prettier, it has flow|babel|babel-flow|babel-ts|typescript|acorn|espree|meriyah|css|less|scss|json|json5|json-stringify|graphql|markdown|mdx|vue|yaml|glimmer|html|angular|lwc parsers

we can not count on a hard coded config to manual calculate the parser to use. this is bad.