helix
helix copied to clipboard
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.
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.
I have a similar issue too which would be solved if there was a way to pass in the filename to the formatter command.
I also have this issue with prettier. Not sure how to get it to format correctly
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"] }
@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.
I got this problem too.
I think provide the full path filename to the formatter is very important.
some formatters need the full file path to help with location a config in the project (stylua) or use it to determine the file type (prettier),
in neovim we can use vim.api.nvim_buf_get_name(0) to get the current buf file path, but in hexlix seems no way to do so.
for example:
style:
--stdin-filepath
Specify the location of the file that is being passed into stdin. Ignored if not taking in input from stdin. This option is only used to help determine where to find the configuration file
require("formatter").setup {
filetype = {
javascript = {
-- prettier
function()
return {
exe = "prettier",
args = { "--stdin-filepath", vim.api.nvim_buf_get_name(0), "--single-quote" },
stdin = true,
}
end,
},
lua = {
-- stylua
function()
return {
exe = "stylua",
args = { "--search-parent-directories", "--stdin-filepath", vim.api.nvim_buf_get_name(0), "-" },
stdin = true,
}
end,
},
}
}
Just adding a +1 to this as a Rubocop user. Without a filename, it's impossible to properly configure rules that only apply to (or are only excepted from) certain files and/or directories.
Reverting https://github.com/helix-editor/helix/pull/2942/commits/f7ecf9c830e1c92a59bb13a11b08a1bb5a7da39e and https://github.com/helix-editor/helix/pull/2942/commits/acdce30bd49405703fcbc0d1012beceb00fbdee7 should add filename formatting.
clang-format would also benefit from being able to pass the filename.
eslint cannot format without stdin-filename
I think this might be more generally-solved by https://github.com/helix-editor/helix/issues/7105
Or, perhaps, a similar approach to that issue: where there is a general value placeholder system, and an initially-fixed set of values we can refer to from within the TOML configuration file, etc
I've encountered two cases where this would be useful:
clang-format-diffneeds 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.- ~
goimportsrequires a filename, not stdin. I think this is because it needs to know the name of the module when sorting imports.~ I'm wrong,goimportsworks over stdin.
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
"""] }
@tynanbe that will only work if you ran hx some_file, right? If you open a different file within helix, it won't know what the file is.
FWIW, using source.organizeImports with https://github.com/helix-editor/helix/pull/6486#issuecomment-1722306737 means I no longer need to run goimports as a formatter, but this would still be great for clang-format-diff.
@rcorre, indeed, I haven't tested that use case, but I personally don't use Helix in that way, so not a problem for me :smile:
Usually for clang-format we execute it like this:
clang-format -i --style=file:<configuration file> <source_file>
While <configuration file> is usually always the same and can be hardcoded, <source file> is usually always different and should be absolute path to the file currently being edited in Helix. So we need a file placeholder to a file currently being edited.
@71GA you can use plain clang-format with helix currently, as it can communicate over stdin/stdout. clangd also supports formatting if you use that as your language server. Typically I just use clangd for C++ formatting, and clang-format with the --assume-filename flag to format protobuf:
[[language]]
name = "protobuf"
formatter = { command = "clang-format" , args = ["--assume-filename=a.proto"]}
It was specifically clang-format-diff (for partially formatted codebases) that I struggled with.
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 have the same problem in Python with Ruff when trying to sort imports.
First-party imports are incorrectly categorized as third-party if stdin is used without the --stdin-filename option.
This leads to inconsistencies between running Ruff as an auto-format command and as a code action.
+1 In my case, the path is required by PowerShell-Beautifier
This is a mandatory feature, especially when the formatter config file is not at the root.
@akhansari pestering the maintainers isn’t going to motivate anyone to get it done. If you feel it’s important it might be wise to focus your efforts on writing a PR instead.
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.