mirror of https://github.com/helix-editor/helix
feat: give formatters access to filename (#13429)
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>pull/13437/head
parent
fbc4e5798e
commit
949d9e4433
|
@ -66,7 +66,7 @@ These configuration keys are available:
|
|||
| `indent` | The indent to use. Has sub keys `unit` (the text inserted into the document when indenting; usually set to N spaces or `"\t"` for tabs) and `tab-width` (the number of spaces rendered for a tab) |
|
||||
| `language-servers` | The Language Servers used for this language. See below for more information in the section [Configuring Language Servers for a language](#configuring-language-servers-for-a-language) |
|
||||
| `grammar` | The tree-sitter grammar to use (defaults to the value of `name`) |
|
||||
| `formatter` | The formatter for the language, it will take precedence over the lsp when defined. The formatter must be able to take the original file as input from stdin and write the formatted file to stdout |
|
||||
| `formatter` | The formatter for the language, it will take precedence over the lsp when defined. The formatter must be able to take the original file as input from stdin and write the formatted file to stdout. The filename of the current buffer can be passed as argument by using the `%{buffer_name}` expansion variable. See below for more information in the [Configuring the formatter command](#configuring-the-formatter-command) |
|
||||
| `soft-wrap` | [editor.softwrap](./editor.md#editorsoft-wrap-section)
|
||||
| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set, defaults to `editor.text-width` |
|
||||
| `rulers` | Overrides the `editor.rulers` config key for the language. |
|
||||
|
@ -102,6 +102,16 @@ with the following priorities:
|
|||
the file extension of a given file wins. In the example above, the `"toml"`
|
||||
config matches files like `Cargo.toml` or `languages.toml`.
|
||||
|
||||
### Configuring the formatter command
|
||||
|
||||
[Command line expansions](./command-line.md#expansions) are supported in the arguments
|
||||
of the formatter command. In particular, the `%{buffer_name}` variable can be passed as
|
||||
argument to the formatter:
|
||||
|
||||
```toml
|
||||
formatter = { command = "mylang-formatter" , args = ["--stdin", "--stdin-filename %{buffer_name}"] }
|
||||
```
|
||||
|
||||
## Language Server configuration
|
||||
|
||||
Language servers are configured separately in the table `language-server` in the same file as the languages `languages.toml`
|
||||
|
|
|
@ -357,7 +357,7 @@ pub struct Token<'a> {
|
|||
pub is_terminated: bool,
|
||||
}
|
||||
|
||||
impl Token<'_> {
|
||||
impl<'a> Token<'a> {
|
||||
pub fn empty_at(content_start: usize) -> Self {
|
||||
Self {
|
||||
kind: TokenKind::Unquoted,
|
||||
|
@ -366,6 +366,15 @@ impl Token<'_> {
|
|||
is_terminated: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand(content: impl Into<Cow<'a, str>>) -> Self {
|
||||
Self {
|
||||
kind: TokenKind::Expand,
|
||||
content_start: 0,
|
||||
content: content.into(),
|
||||
is_terminated: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -339,8 +339,9 @@ fn write_impl(cx: &mut compositor::Context, path: Option<&str>, force: bool) ->
|
|||
// Save an undo checkpoint for any outstanding changes.
|
||||
doc.append_changes_to_history(view);
|
||||
|
||||
let (view, doc) = current_ref!(cx.editor);
|
||||
let fmt = if config.auto_format {
|
||||
doc.auto_format().map(|fmt| {
|
||||
doc.auto_format(cx.editor).map(|fmt| {
|
||||
let callback = make_format_callback(
|
||||
doc.id(),
|
||||
doc.version(),
|
||||
|
@ -483,8 +484,8 @@ fn format(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyh
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let format = doc.format().context(
|
||||
let (view, doc) = current_ref!(cx.editor);
|
||||
let format = doc.format(cx.editor).context(
|
||||
"A formatter isn't available, and no language server provides formatting capabilities",
|
||||
)?;
|
||||
let callback = make_format_callback(doc.id(), doc.version(), view.id, format, None);
|
||||
|
@ -752,7 +753,8 @@ pub fn write_all_impl(
|
|||
doc.append_changes_to_history(view);
|
||||
|
||||
let fmt = if options.auto_format && config.auto_format {
|
||||
doc.auto_format().map(|fmt| {
|
||||
let doc = doc!(cx.editor, &doc_id);
|
||||
doc.auto_format(cx.editor).map(|fmt| {
|
||||
let callback = make_format_callback(
|
||||
doc_id,
|
||||
doc.version(),
|
||||
|
|
|
@ -5,6 +5,7 @@ use futures_util::future::BoxFuture;
|
|||
use futures_util::FutureExt;
|
||||
use helix_core::auto_pairs::AutoPairs;
|
||||
use helix_core::chars::char_is_word;
|
||||
use helix_core::command_line::Token;
|
||||
use helix_core::diagnostic::DiagnosticProvider;
|
||||
use helix_core::doc_formatter::TextFormat;
|
||||
use helix_core::encoding::Encoding;
|
||||
|
@ -45,6 +46,7 @@ use helix_core::{
|
|||
use crate::{
|
||||
editor::Config,
|
||||
events::{DocumentDidChange, SelectionDidChange},
|
||||
expansion,
|
||||
view::ViewPosition,
|
||||
DocumentId, Editor, Theme, View, ViewId,
|
||||
};
|
||||
|
@ -777,9 +779,12 @@ impl Document {
|
|||
|
||||
/// The same as [`format`], but only returns formatting changes if auto-formatting
|
||||
/// is configured.
|
||||
pub fn auto_format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
|
||||
pub fn auto_format(
|
||||
&self,
|
||||
editor: &Editor,
|
||||
) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
|
||||
if self.language_config()?.auto_format {
|
||||
self.format()
|
||||
self.format(editor)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -789,7 +794,10 @@ impl Document {
|
|||
/// to format it nicely.
|
||||
// We can't use anyhow::Result here since the output of the future has to be
|
||||
// clonable to be used as shared future. So use a custom error type.
|
||||
pub fn format(&self) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
|
||||
pub fn format(
|
||||
&self,
|
||||
editor: &Editor,
|
||||
) -> Option<BoxFuture<'static, Result<Transaction, FormatterError>>> {
|
||||
if let Some((fmt_cmd, fmt_args)) = self
|
||||
.language_config()
|
||||
.and_then(|c| c.formatter.as_ref())
|
||||
|
@ -814,8 +822,20 @@ impl Document {
|
|||
process.current_dir(doc_dir);
|
||||
}
|
||||
|
||||
let args = match fmt_args
|
||||
.iter()
|
||||
.map(|content| expansion::expand(editor, Token::expand(content)))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
{
|
||||
Ok(args) => args,
|
||||
Err(err) => {
|
||||
log::error!("Failed to expand formatter arguments: {err}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
process
|
||||
.args(fmt_args)
|
||||
.args(args.iter().map(AsRef::as_ref))
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
|
Loading…
Reference in New Issue