├── .gitignore ├── admin └── check.el ├── LICENSE ├── README.md └── format-all.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /admin/check.el: -------------------------------------------------------------------------------- 1 | (with-temp-buffer 2 | (insert-file-contents "../format-all.el") 3 | (let (forms) 4 | (condition-case () (while t (push (read (current-buffer)) forms)) 5 | (end-of-file (setq forms (reverse forms)))) 6 | (let* ((formatter-names 7 | (reverse 8 | (cl-reduce 9 | (lambda (names form) 10 | (if (and (consp form) 11 | (eql 'define-format-all-formatter (cl-first form))) 12 | (cons (cl-second form) names) 13 | names)) 14 | forms :initial-value '()))) 15 | (sorted-names (sort (cl-copy-list formatter-names) 'string<))) 16 | (or (and (equal formatter-names sorted-names) 17 | (message "We have %d formatters" (length formatter-names))) 18 | (error "Formatter names are not in alphabetical order"))))) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2018-2019 format-all.el authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *format-all* for Emacs 2 | ====================== 3 | 4 | > *NOTE:* The package is actively maintained but due to lack of time, 5 | > complex tasks are done at a slow pace. Simple tasks like adding or 6 | > fixing formatter definitions are often done immediately. For faster 7 | > progress, additional maintainers are welcome. 8 | 9 | What does it do 10 | --------------- 11 | 12 | Lets you auto-format source code in many languages using the same 13 | command for all languages, instead of learning a different Emacs 14 | package and formatting command for each language. 15 | 16 | Just do **M-x** `format-all-region-or-buffer` and it will try its best 17 | to do the right thing. To auto-format code on save, use the minor mode 18 | `format-all-mode`. Please see the documentation for that function for 19 | instructions. 20 | 21 | Supported languages 22 | ------------------- 23 | 24 | * **Angular** ([*prettier*](https://prettier.io/)) 25 | * **Assembly** ([*asmfmt*](https://github.com/klauspost/asmfmt)) 26 | * **ATS** ([*atsfmt*](https://hackage.haskell.org/package/ats-format)) 27 | * **Awk** ([*gawk*](https://www.gnu.org/software/gawk/)) 28 | * **AZSL** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html)) 29 | * **Bazel Starlark** ([*buildifier*](https://github.com/bazelbuild/buildtools/tree/master/buildifier)) 30 | * **Beancount** ([*bean-format*](https://beancount.github.io/)) 31 | * **BibTeX** (Emacs) 32 | * **C/C++/Objective-C** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html), [*astyle*](http://astyle.sourceforge.net/)) 33 | * **C#** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html), [*astyle*](http://astyle.sourceforge.net/), [*csharpier*](https://github.com/belav/csharpier)) 34 | * **Cabal** ([*cabal-fmt*](https://github.com/phadej/cabal-fmt)) 35 | * **Caddyfile** ([*caddy-fmt*](https://caddyserver.com/docs/command-line#caddy-fmt)) 36 | * **Clojure/ClojureScript** ([*cljfmt*](https://github.com/weavejester/cljfmt), [*zprint*](https://github.com/kkinnear/zprint)) 37 | * **CMake** ([*cmake-format*](https://github.com/cheshirekow/cmake_format)) 38 | * **Crystal** ([*crystal tool format*](http://www.motion-express.com/blog/crystal-code-formatter)) 39 | * **CSS/Less/SCSS** ([*prettier*](https://prettier.io/), [*prettierd*](https://github.com/fsouza/prettierd)) 40 | * **Cuda** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html)) 41 | * **D** ([*dfmt*](https://github.com/dlang-community/dfmt)) 42 | * **Dart** ([*dartfmt*](https://github.com/dart-lang/dart_style), [*dart-format*](https://dart.dev/tools/dart-format)) 43 | * **Dhall** ([*dhall format*](https://github.com/dhall-lang/dhall-lang)) 44 | * **Dockerfile** ([*dockfmt*](https://github.com/jessfraz/dockfmt)) 45 | * **Elixir** ([*mix format*](https://hexdocs.pm/mix/master/Mix.Tasks.Format.html)) 46 | * **Elm** ([*elm-format*](https://github.com/avh4/elm-format)) 47 | * **Emacs Lisp** (Emacs) 48 | * **Erb** ([*erb-format*](https://github.com/nebulab/erb-formatter)) 49 | * **Erlang** ([*efmt*](https://github.com/sile/efmt)) 50 | * **F#** ([*fantomas*](https://github.com/fsprojects/fantomas)) 51 | * **Fish Shell** ([*fish_indent*](https://fishshell.com/docs/current/commands.html#fish_indent)) 52 | * **Fortran Free Form** ([*fprettify*](https://github.com/pseewald/fprettify)) 53 | * **Gleam** ([*gleam format*](https://gleam.run/)) 54 | * **GLSL** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html)) 55 | * **Go** ([*gofmt*](https://golang.org/cmd/gofmt/), [*goimports*](https://godoc.org/golang.org/x/tools/cmd/goimports)) 56 | * **GraphQL** ([*prettier*](https://prettier.io/), [*prettierd*](https://github.com/fsouza/prettierd)) 57 | * **Haskell** ([*brittany*](https://github.com/lspitzner/brittany), [*fourmolu*](https://github.com/fourmolu/fourmolu), [*hindent*](https://github.com/commercialhaskell/hindent), [*ormolu*](https://github.com/tweag/ormolu), [*stylish-haskell*](https://github.com/jaspervdj/stylish-haskell)) 58 | * **HCL** ([*hclfmt*](https://github.com/hashicorp/hcl/tree/main/cmd/hclfmt)) 59 | * **HLSL** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html)) 60 | * **HTML/XHTML/XML** ([*tidy*](http://www.html-tidy.org/)) 61 | * **Hy** (Emacs) 62 | * **Java** ([*astyle*](http://astyle.sourceforge.net/), [*clang-format*](https://clang.llvm.org/docs/ClangFormat.html), [*google-java-format*](https://github.com/google/google-java-format)) 63 | * **JavaScript/JSON/JSX** ([*prettier*](https://prettier.io/), [*standard*](https://standardjs.com/), [*prettierd*](https://github.com/fsouza/prettierd), [*deno*](https://deno.land/manual/tools/formatter)) 64 | * **Jsonnet** ([*jsonnetfmt*](https://jsonnet.org/)) 65 | * **Kotlin** ([*ktlint*](https://github.com/shyiko/ktlint)) 66 | * **LaTeX** ([*latexindent*](https://github.com/cmhughes/latexindent.pl), [*auctex*](https://www.gnu.org/software/auctex/)) 67 | * **Ledger** ([*ledger-mode*](https://github.com/ledger/ledger-mode)) 68 | * **Lua** ([*lua-fmt*](https://github.com/trixnz/lua-fmt), [stylua](https://github.com/JohnnyMorganz/StyLua), [*prettier plugin*](https://github.com/prettier/plugin-lua)) 69 | * **Markdown** ([*prettier*](https://prettier.io/), [*prettierd*](https://github.com/fsouza/prettierd), [*deno*](https://deno.land/manual/tools/formatter)) 70 | * **Meson** ([*muon fmt*](https://sr.ht/~lattis/muon/)) 71 | * **Nginx** ([*nginxfmt*](https://github.com/slomkowski/nginx-config-formatter)) 72 | * **Nix** ([*nixpkgs-fmt*](https://github.com/nix-community/nixpkgs-fmt), [*nixfmt*](https://github.com/serokell/nixfmt), 73 | [*alejandra*](https://github.com/kamadorueda/alejandra)) 74 | * **OCaml** ([*ocp-indent*](https://opam.ocaml.org/packages/ocp-indent/), [*ocamlformat*](https://github.com/ocaml-ppx/ocamlformat)) 75 | * **Perl** ([*perltidy*](http://perltidy.sourceforge.net/)) 76 | * **PHP** ([*prettier plugin*](https://github.com/prettier/plugin-php)) 77 | * **Protocol Buffers** ([*clang-format*](https://clang.llvm.org/docs/ClangFormat.html)) 78 | * **PureScript** ([*purty*](https://gitlab.com/joneshf/purty), [*purescript-tidy*](https://github.com/natefaubion/purescript-tidy)) 79 | * **Python** ([*black*](https://github.com/ambv/black), [*isort*](https://github.com/PyCQA/isort), [*ruff format*](https://docs.astral.sh/ruff/formatter/), [*yapf*](https://github.com/google/yapf)) 80 | * **R** ([*styler*](https://github.com/r-lib/styler)) 81 | * **Racket** ([*raco fmt*](https://docs.racket-lang.org/fmt/)) 82 | * **Reason** ([*bsrefmt*](https://github.com/glennsl/bs-refmt)) 83 | * **ReScript** ([*rescript format*](https://www.npmjs.com/package/rescript)) 84 | * **Ruby** ([*rubocop*](https://github.com/rubocop/rubocop), [*rufo*](https://github.com/ruby-formatter/rufo), [*standardrb*](https://github.com/testdouble/standard), [*stree (syntax_tree)*](https://github.com/ruby-syntax-tree/syntax_tree)) 85 | * **Rust** ([*rustfmt*](https://github.com/rust-lang-nursery/rustfmt)) 86 | * **Scala** ([*scalafmt*](https://github.com/scalameta/scalafmt)) 87 | * **Shell script** ([*beautysh*](https://github.com/lovesegfault/beautysh), [*shfmt*](https://github.com/mvdan/sh)) 88 | * **Snakemake** ([*snakefmt*](https://github.com/snakemake/snakefmt)) 89 | * **Solidity** ([*prettier plugin*](https://github.com/prettier-solidity/prettier-plugin-solidity)) 90 | * **SQL** ([*pgformatter*](https://github.com/darold/pgFormatter), [*sqlformat*](https://pypi.org/project/sqlparse/)) 91 | * **Svelte** ([*prettier plugin*](https://github.com/sveltejs/prettier-plugin-svelte)) 92 | * **Swift** ([*swiftformat*](https://github.com/nicklockwood/SwiftFormat)) 93 | * **Terraform** ([*terraform fmt*](https://www.terraform.io/docs/commands/fmt.html)) 94 | * **TOML** ([*prettier plugin*](https://github.com/bd82/toml-tools/tree/master/packages/prettier-plugin-toml), [*taplo fmt*](https://taplo.tamasfe.dev/cli/usage/formatting.html)) 95 | * **TypeScript/TSX** ([*prettier*](https://prettier.io/), [*ts-standard*](https://standardjs.com/), [*prettierd*](https://github.com/fsouza/prettierd), [*deno*](https://deno.land/manual/tools/formatter)) 96 | * **V** ([*v fmt*](https://github.com/vlang/v)) 97 | * **Vue** ([*prettier*](https://prettier.io/), [*prettierd*](https://github.com/fsouza/prettierd)) 98 | * **Verilog** ([*iStyle*](https://github.com/thomasrussellmurphy/istyle-verilog-formatter), [*Verible*](https://github.com/chipsalliance/verible/tree/master/verilog/tools/formatter)) 99 | * **YAML** ([*prettier*](https://prettier.io/), [*prettierd*](https://github.com/fsouza/prettierd)) 100 | * **Zig** ([*zig*](https://ziglang.org/)) 101 | 102 | How to install 103 | -------------- 104 | 105 | From [MELPA](https://melpa.org/#/format-all) 106 | 107 | You will need to install external programs to do the formatting. If 108 | `format-all-buffer` can't find the right program, it will try to tell 109 | you how to install it. 110 | 111 | If you have installed a formatter but Emacs cannot find it, Emacs may 112 | be using a `PATH` different from your shell. The path searched by 113 | Emacs is in the `exec-path` variable. You can easily make it match 114 | your shell's `PATH` using the 115 | [exec-path-from-shell](http://melpa.org/#/exec-path-from-shell) 116 | package from MELPA. 117 | 118 | How to customize 119 | ---------------- 120 | 121 | `M-x customize-group format-all` has a few basic settings. 122 | 123 | However, the main thing you probably want to set is 124 | `format-all-formatters`. That variable is buffer-local, and can be 125 | made project-local by setting it in a `.dir-locals.el` file in a 126 | project's directory. That file can be committed to version control to 127 | share it with the whole project. 128 | 129 | To enable format on save for most programming language buffers: 130 | `(add-hook 'prog-mode-hook 'format-all-mode)`. 131 | 132 | To control displaying the formatting errors buffer when 133 | formatting fails or has warnings, customize the variable 134 | `format-all-show-errors`. Set it to one of these - `'always` (shows 135 | errors buffer regardless),`'warnings` (shows errors buffer for 136 | both errors and warnings), `'errors` (only show errors buffer 137 | when there are errors) or `'never` (never show errors buffer). 138 | 139 | The command `format-all-ensure-formatter` will ensure that a default 140 | formatter is selected in case you don't have one set; you can 141 | customize the default formatter for each language. To ensure a 142 | formatter is set whenever you enable `format-all-mode`, you can use: 143 | `(add-hook 'format-all-mode-hook 'format-all-ensure-formatter)`. 144 | 145 | Additionally, many of the external formatters support configuration 146 | files in the source code directory to control their formatting. Please 147 | see the documentation for each formatter. 148 | 149 | Examples 150 | -------- 151 | 152 | ### Simple example 153 | 154 | (setq format-all-formatters 155 | '(("Shell" (shfmt "-i" "4" "-ci")))) 156 | 157 | ### Setting default formatters with `use-package` 158 | 159 | (use-package format-all 160 | :commands format-all-mode 161 | :hook (prog-mode . format-all-mode) 162 | :config 163 | (setq-default format-all-formatters 164 | '(("C" (astyle "--mode=c")) 165 | ("Shell" (shfmt "-i" "4" "-ci"))))) 166 | 167 | This config will assure that: 168 | 169 | 1. `format-all` will be loaded after `format-all-mode` command 170 | 2. `format-all-mode` will be executed each time you enter a mode that emacs recognized as designed for programing 171 | 3. only after `format-all` is loaded it will set `format-all-formatters` globally for all buffers 172 | 173 | Alternatively you can replace `:config` with `:init` and `setq-default` with `setq`. It will also work but will be less efficient. 174 | 175 | [comment]: <> (I think this sentence either needs expanding or deleting ^) 176 | 177 | ### Setting the default formatter for each mode 178 | 179 | This approach allows you to split your default formatters accross many 180 | places in your config. 181 | 182 | (add-hook 'java-mode-hook 183 | (lambda () 184 | (setq format-all-formatters 185 | '(("Java" (astyle "--mode=java")))))) 186 | 187 | If you want to optimize your config to defer setting variables, you may 188 | remove the `:config` section from `use-package` snippet and use this variant 189 | 190 | (eval-after-load 'format-all 191 | '(add-hook 'java-mode-hook 192 | (lambda () 193 | (setq format-all-formatters 194 | '(("Java" (astyle "--mode=java"))))))) 195 | 196 | ### sqlformat 197 | 198 | The formatter `sqlformat` confuses many people. It does not to do any 199 | formatting by default. When you run it, nothing seems to happen. 200 | 201 | This appears to be a deliberate design decision. Please contact the 202 | maintainer of `sqlformat` if you would like it to change. 203 | 204 | You can add your own command line flags using a hook. The following 205 | example sets the `-r` option. 206 | 207 | (add-hook 'sql-mode-hook 208 | (lambda () 209 | (setq format-all-formatters 210 | '(("SQL" (sqlformat "-r")))))) 211 | 212 | How to add new languages 213 | ------------------------ 214 | 215 | New external formatters can be added easily if they can read code from 216 | standard input and format it to standard output. Feel free to submit a 217 | pull request or ask for help in [GitHub issues][github-issues]. 218 | 219 | How to report bugs 220 | ------------------ 221 | 222 | [GitHub issues][github-issues] are preferred. Email is also ok. 223 | 224 | Feature requests are welcome. If you are interested in doing anything 225 | beyond adding new formatters in the current framework, please discuss 226 | in issues before writing code. 227 | 228 | Roadmap 229 | ------- 230 | 231 | **[atom-beautify](https://atom.io/packages/atom-beautify#beautifiers)** 232 | sports a very impressive set of formatters. We should aspire to that 233 | level of coverage for Emacs. 234 | 235 | **[Unibeautify](https://github.com/Unibeautify/unibeautify)** is a 236 | project to provide one shell command to run all beautifiers. 237 | *atom-beautify* will be rewritten to be based on it. Perhaps we should 238 | be too, once it stabilizes. 239 | 240 | [github-issues]: https://github.com/lassik/emacs-format-all-the-code/issues 241 | -------------------------------------------------------------------------------- /format-all.el: -------------------------------------------------------------------------------- 1 | ;;; format-all.el --- Auto-format C, C++, JS, Python, Ruby and 50 other languages -*- lexical-binding: t -*- 2 | 3 | ;; Author: Lassi Kortela 4 | ;; URL: https://github.com/lassik/emacs-format-all-the-code 5 | ;; Version: 0.6.0 6 | ;; Package-Requires: ((emacs "24.4") (inheritenv "0.1") (language-id "0.20")) 7 | ;; Keywords: languages util 8 | ;; SPDX-License-Identifier: MIT 9 | 10 | ;; This file is not part of GNU Emacs. 11 | 12 | ;;; Commentary: 13 | 14 | ;; Lets you auto-format source code in many languages using the same 15 | ;; command for all languages, instead of learning a different Emacs 16 | ;; package and formatting command for each language. 17 | 18 | ;; Just do M-x format-all-buffer and it will try its best to do the 19 | ;; right thing. To auto-format code on save, use the minor mode 20 | ;; format-all-mode. Please see the documentation for that function 21 | ;; for instructions. 22 | 23 | ;; Supported languages: 24 | 25 | ;; - Angular (prettier) 26 | ;; - Assembly (asmfmt) 27 | ;; - ATS (atsfmt) 28 | ;; - Awk (gawk) 29 | ;; - AZSL (clang-format) 30 | ;; - Bazel Starlark (buildifier) 31 | ;; - Beancount (bean-format) 32 | ;; - BibTeX (Emacs) 33 | ;; - C/C++/Objective-C (clang-format, astyle) 34 | ;; - C# (clang-format, astyle, csharpier) 35 | ;; - Cabal (cabal-fmt) 36 | ;; - Caddyfile (caddy fmt) 37 | ;; - Clojure/ClojureScript (cljfmt, zprint) 38 | ;; - CMake (cmake-format) 39 | ;; - Crystal (crystal tool format) 40 | ;; - CSS/Less/SCSS (prettier, prettierd) 41 | ;; - Cuda (clang-format) 42 | ;; - D (dfmt) 43 | ;; - Dart (dartfmt, dart-format) 44 | ;; - Dhall (dhall format) 45 | ;; - Dockerfile (dockfmt) 46 | ;; - Elixir (mix format) 47 | ;; - Elm (elm-format) 48 | ;; - Emacs Lisp (Emacs) 49 | ;; - Erb (erb-format) 50 | ;; - Erlang (efmt) 51 | ;; - F# (fantomas) 52 | ;; - Fish Shell (fish_indent) 53 | ;; - Fortran Free Form (fprettify) 54 | ;; - Gleam (gleam format) 55 | ;; - GLSL (clang-format) 56 | ;; - Go (gofmt, goimports) 57 | ;; - GraphQL (prettier, prettierd) 58 | ;; - Haskell (brittany, fourmolu, hindent, ormolu, stylish-haskell) 59 | ;; - HCL (hclfmt) 60 | ;; - HLSL (clang-format) 61 | ;; - HTML/XHTML/XML (tidy) 62 | ;; - Hy (Emacs) 63 | ;; - Java (astyle, clang-format, google-java-format) 64 | ;; - JavaScript/JSON/JSX (prettier, standard, prettierd, deno) 65 | ;; - Jsonnet (jsonnetfmt) 66 | ;; - Kotlin (ktlint) 67 | ;; - LaTeX (latexindent, auctex) 68 | ;; - Ledger (ledger-mode) 69 | ;; - Lua (lua-fmt, stylua, prettier plugin) 70 | ;; - Markdown (prettier, prettierd, deno) 71 | ;; - Meson (muon fmt) 72 | ;; - Nginx (nginxfmt) 73 | ;; - Nix (nixpkgs-fmt, nixfmt, alejandra) 74 | ;; - OCaml (ocp-indent, ocamlformat) 75 | ;; - Perl (perltidy) 76 | ;; - PHP (prettier plugin) 77 | ;; - Protocol Buffers (clang-format) 78 | ;; - PureScript (purty, purs-tidy) 79 | ;; - Python (black, isort, ruff format, yapf) 80 | ;; - R (styler) 81 | ;; - Racket (raco-fmt) 82 | ;; - Reason (bsrefmt) 83 | ;; - ReScript (rescript) 84 | ;; - Ruby (rubocop, rufo, standardrb, stree) 85 | ;; - Rust (rustfmt) 86 | ;; - Scala (scalafmt) 87 | ;; - Shell script (beautysh, shfmt) 88 | ;; - Snakemake (snakefmt) 89 | ;; - Solidity (prettier plugin) 90 | ;; - SQL (pgformatter, sqlformat) 91 | ;; - Svelte (prettier plugin) 92 | ;; - Swift (swiftformat) 93 | ;; - Terraform (terraform fmt) 94 | ;; - TOML (prettier plugin, taplo fmt) 95 | ;; - TypeScript/TSX (prettier, ts-standard, prettierd, deno) 96 | ;; - V (v fmt) 97 | ;; - Vue (prettier, prettierd) 98 | ;; - Verilog (iStyle, Verible) 99 | ;; - YAML (prettier, prettierd) 100 | ;; - Zig (zig) 101 | 102 | ;; You will need to install external programs to do the formatting. 103 | ;; If `format-all-buffer` can't find the right program, it will try to 104 | ;; tell you how to install it. 105 | 106 | ;; Many of the external formatters support configuration files in the 107 | ;; source code directory to control their formatting. Please see the 108 | ;; documentation for each formatter. 109 | 110 | ;; New external formatters can be added easily if they can read code 111 | ;; from standard input and format it to standard output. Feel free to 112 | ;; submit a pull request or ask for help in GitHub issues. 113 | 114 | ;;; Code: 115 | 116 | (require 'subr-x) 117 | (require 'cl-lib) 118 | (require 'inheritenv) 119 | (require 'language-id) 120 | 121 | (defgroup format-all nil 122 | "Lets you auto-format source code." 123 | :group 'format-all) 124 | 125 | (defcustom format-all-debug nil 126 | "When non-nil, troubleshooting info is written into the *Messages* buffer." 127 | :type 'boolean 128 | :group 'format-all) 129 | 130 | (defcustom format-all-default-formatters 131 | '(("Assembly" asmfmt) 132 | ("ATS" atsfmt) 133 | ("Bazel" buildifier) 134 | ("BibTeX" emacs-bibtex) 135 | ("C" clang-format) 136 | ("C#" csharpier) 137 | ("C++" clang-format) 138 | ("Cabal Config" cabal-fmt) 139 | ("Clojure" zprint) 140 | ("CMake" cmake-format) 141 | ("Crystal" crystal) 142 | ("CSS" prettier) 143 | ("Cuda" clang-format) 144 | ("D" dfmt) 145 | ("Dart" dart-format) 146 | ("Dhall" dhall) 147 | ("Dockerfile" dockfmt) 148 | ("Elixir" mix-format) 149 | ("Elm" elm-format) 150 | ("Emacs Lisp" emacs-lisp) 151 | ("Erlang" efmt) 152 | ("F#" fantomas) 153 | ("Fish" fish-indent) 154 | ("Fortran Free Form" fprettify) 155 | ("GLSL" clang-format) 156 | ("Go" gofmt) 157 | ("GraphQL" prettier) 158 | ("Haskell" brittany) 159 | ("HCL" hclfmt) 160 | ("HLSL" clang-format) 161 | ("HTML" html-tidy) 162 | ("HTML+EEX" mix-format) 163 | ("HTML+ERB" erb-format) 164 | ("Hy" emacs-hy) 165 | ("Java" google-java-format) 166 | ("JavaScript" prettier) 167 | ("JSON" prettier) 168 | ("JSON5" prettier) 169 | ("Jsonnet" jsonnetfmt) 170 | ("JSX" prettier) 171 | ("Kotlin" ktlint) 172 | ("LaTeX" latexindent) 173 | ("Less" prettier) 174 | ("Literate Haskell" brittany) 175 | ("Lua" lua-fmt) 176 | ("Markdown" prettier) 177 | ("Meson" muon-fmt) 178 | ("Nix" nixpkgs-fmt) 179 | ("Objective-C" clang-format) 180 | ("OCaml" ocp-indent) 181 | ("Perl" perltidy) 182 | ("PHP" prettier) 183 | ("Protocol Buffer" clang-format) 184 | ("PureScript" purty) 185 | ("Python" black) 186 | ("R" styler) 187 | ("Reason" bsrefmt) 188 | ("ReScript" rescript) 189 | ("Ruby" rufo) 190 | ("Rust" rustfmt) 191 | ("Scala" scalafmt) 192 | ("SCSS" prettier) 193 | ("Shell" shfmt) 194 | ("Solidity" prettier) 195 | ("SQL" sqlformat) 196 | ("Svelte" prettier) 197 | ("Swift" swiftformat) 198 | ("Terraform" terraform-fmt) 199 | ("TOML" prettier) 200 | ("TSX" prettier) 201 | ("TypeScript" prettier) 202 | ("V" v-fmt) 203 | ("Verilog" istyle-verilog) 204 | ("Vue" prettier) 205 | ("XML" html-tidy) 206 | ("YAML" prettier) 207 | ("Zig" zig) 208 | 209 | ("_Angular" prettier) 210 | ("_AZSL" clang-format) 211 | ("_Beancount" bean-format) 212 | ("_Caddyfile" caddy-fmt) 213 | ("_Flow" prettier) 214 | ("_Gleam" gleam) 215 | ("_Ledger" ledger-mode) 216 | ("_Nginx" nginxfmt) 217 | ("_Snakemake" snakefmt)) 218 | "Default formatter to use for each language." 219 | :type '(repeat (list (string :tag "Language") 220 | (choice (symbol :tag "Formatter Only") 221 | (cons :tag "Formatter with Custom Arguments" 222 | (symbol :tag "Formatter") 223 | (repeat :tag "Custom Arguments" 224 | (string :tag "Argument")))))) 225 | :group 'format-all) 226 | 227 | (defcustom format-all-show-errors 'errors 228 | "When to show formatting errors or warnings." 229 | :type '(choice (const :tag "Always" always) 230 | (const :tag "Errors" errors) 231 | (const :tag "Warnings" warnings) 232 | (const :tag "Never" never)) 233 | :group 'format-all) 234 | 235 | (defcustom format-all-mode-lighter " FmtAll" 236 | "Lighter for command `format-all-mode'." 237 | :type '(string :tag "Lighter") 238 | :risky t 239 | :group 'format-all) 240 | 241 | (defvar format-all-after-format-functions nil 242 | "Hook run after each time `format-all-buffer' has formatted a buffer. 243 | 244 | The value is a list of hook functions. Use `add-hook' to add a 245 | function. The function is called with two arguments: (FORMATTER 246 | STATUS). FORMATTER is a symbol naming the formatter, as given to 247 | `define-format-all-formatter'. STATUS is one of the following 248 | keywords: 249 | 250 | * :reformatted -- The formatter made changes to the buffer. 251 | 252 | * :already-formatted -- The buffer was already formatted 253 | correctly so the formatter didn't make any changes to it. 254 | 255 | * :error -- The formatter encountered an error (usually a syntax 256 | error). The buffer contents are the same as before formatting. 257 | 258 | The current buffer is the buffer that was just formatted. Point 259 | is not guaranteed to be in any particular place, so `goto-char' 260 | before editing the buffer. Narrowing may be in effect unless 261 | STATUS is :reformatted.") 262 | 263 | (defvar format-all--user-args nil 264 | "Internal variable to temporarily store arguments for formatters.") 265 | 266 | (defvar-local format-all-formatters nil 267 | "Rules to select which formatter format-all uses. 268 | 269 | The value is an association list. 270 | 271 | The first item of each association is the name of a programming 272 | language. (GitHub Linguist names are used.) 273 | 274 | The remaining items are one or more formatters to use for that 275 | language. Each formatter is either: 276 | 277 | * a symbol (e.g. black, clang-format, rufo) 278 | 279 | * a list whose first item is that symbol, and any remaining items 280 | are extra command line arguments to pass to the formatter 281 | 282 | If more than one formatter is given for the same language, all of 283 | them are run as a chain, with the code from each formatter passed 284 | to the next. The final code is from the last formatter. In case 285 | any formatter in the chain is missing or fails to format the 286 | code, the entire chain fails and the old code before formatting 287 | is preserved. 288 | 289 | You'll probably want to set this in a \".dir-locals.el\" file or 290 | in a hook function. Any number of buffers can share the same 291 | association list. Using \".dir-locals.el\" is convenient since 292 | the rules for an entire source tree can be given in one file.") 293 | 294 | (define-error 'format-all-executable-not-found 295 | "Formatter not found") 296 | 297 | (defun format-all--proper-list-p (object) 298 | "Return t if OBJECT is a proper list, nil otherwise." 299 | ;; If we could depend on Emacs 27.1 this function would be built in. 300 | (condition-case _ (not (null (cl-list-length object))) 301 | (wrong-type-argument nil))) 302 | 303 | (defun format-all--normalize-formatter (formatter) 304 | "Internal function to convert FORMATTER spec into normal form." 305 | (let ((formatter (if (listp formatter) formatter (list formatter)))) 306 | (when (cdr (last formatter)) 307 | (error "Formatter is not a proper list: %S" formatter)) 308 | (when (null formatter) 309 | (error "Formatter name missing")) 310 | (unless (symbolp (car formatter)) 311 | (error "Formatter name is not a symbol: %S" (car formatter))) 312 | (unless (cl-every #'stringp (cdr formatter)) 313 | (error "Formatter command line arguments are not all strings: %S" 314 | formatter)) 315 | formatter)) 316 | 317 | (defun format-all--normalize-chain (chain) 318 | "Internal function to convert CHAIN spec into normal form." 319 | (when (or (not (listp chain)) (cdr (last chain))) 320 | (error "Formatter chain is not a proper list: %S" chain)) 321 | (mapcar #'format-all--normalize-formatter chain)) 322 | 323 | (defun format-all-valid-formatters-p (formatters) 324 | "Return t if FORMATTERS is a valid value for `format-all-formatters'." 325 | (and (format-all--proper-list-p formatters) 326 | (cl-every 327 | (lambda (chain) 328 | (and (not (null chain)) 329 | (format-all--proper-list-p chain) 330 | (stringp (car chain)) 331 | (cl-every 332 | (lambda (formatter) 333 | (and (not (null formatter)) 334 | (or (symbolp formatter) 335 | (and (format-all--proper-list-p formatter) 336 | (and (symbolp (car formatter)) 337 | (not (null (car formatter)))) 338 | (cl-every #'stringp (cdr formatter)))))) 339 | (cdr chain)))) 340 | formatters))) 341 | 342 | (put 'format-all-formatters 'safe-local-variable 343 | 'format-all-valid-formatters-p) 344 | 345 | (eval-and-compile 346 | (defconst format-all--system-type 347 | (cl-case system-type 348 | (windows-nt 'windows) 349 | (cygwin 'windows) 350 | (darwin 'macos) 351 | (gnu/linux 'linux) 352 | (berkeley-unix 353 | (save-match-data 354 | (let ((case-fold-search t)) 355 | (cond ((string-match "freebsd" system-configuration) 'freebsd) 356 | ((string-match "openbsd" system-configuration) 'openbsd) 357 | ((string-match "netbsd" system-configuration) 'netbsd)))))) 358 | "Current operating system according to the format-all package.")) 359 | 360 | (eval-and-compile 361 | (defun format-all--resolve-system (choices) 362 | "Get first choice matching `format-all--system-type' from CHOICES." 363 | (cl-dolist (choice choices) 364 | (cond ((atom choice) 365 | (cl-return choice)) 366 | ((eql format-all--system-type (car choice)) 367 | (cl-return (cadr choice))))))) 368 | 369 | (defun format-all--fix-trailing-whitespace () 370 | "Fix trailing whitespace since some formatters don't do that." 371 | (save-match-data 372 | (goto-char (point-min)) 373 | (while (re-search-forward "[ \t]+$" nil t) 374 | (replace-match "")) 375 | (goto-char (point-max)) 376 | (delete-region 377 | (if (re-search-backward "[^ \t\n]" nil t) (match-end 0) (point-min)) 378 | (point-max)) 379 | (unless (= (point-min) (point-max)) 380 | (goto-char (point-max)) 381 | (insert "\n")))) 382 | 383 | (defun format-all--remove-ansi-color (string) 384 | "Internal helper function to remove terminal color codes from STRING." 385 | (save-match-data (replace-regexp-in-string "\x1b\\[[0-9]+m" "" string t))) 386 | 387 | (defun format-all--flatten-once (list) 388 | "Internal helper function to remove nested lists in LIST." 389 | (cl-mapcan (lambda (x) (if (listp x) x (list x))) 390 | list)) 391 | 392 | (defun format-all--buffer-extension-p (&rest extensions) 393 | "Internal helper function to test file name EXTENSIONS." 394 | (and (buffer-file-name) 395 | (save-match-data 396 | (let ((case-fold-search t)) 397 | (cl-some (lambda (ext) 398 | (string-match (concat "\\." (regexp-quote ext) "\\'") 399 | (buffer-file-name))) 400 | extensions))))) 401 | 402 | (defun format-all--buffer-thunk (thunk) 403 | "Internal helper function to implement formatters. 404 | 405 | THUNK is a function that implements a particular formatter. It 406 | takes INPUT (the unformatted source code as a string). THUNK is 407 | invoked such that the current buffer is an empty temp buffer. It 408 | should call the formatter on INPUT and write the formatted source 409 | code output to the temp buffer. It should return (ERRORP 410 | ERROR-OUTPUT). ERRORP is a boolean indicating whether the formatter 411 | caused an error and hence the contents of the temp buffer should 412 | be discarded. ERROR-OUTPUT is a string containing all error/warning 413 | output from the formatter. 414 | 415 | Note that in some cases we can use the output of the formatter 416 | even if it produced warnings. Not all warnings are errors." 417 | (save-excursion 418 | (save-restriction 419 | (widen) 420 | (let ((inbuf (current-buffer)) 421 | (input (buffer-string))) 422 | (inheritenv 423 | (with-temp-buffer 424 | (cl-destructuring-bind (errorp error-output) (funcall thunk input) 425 | (let* ((no-chg (or errorp 426 | (= 0 (let ((case-fold-search nil)) 427 | (compare-buffer-substrings 428 | inbuf nil nil nil nil nil))))) 429 | (output (cond (errorp nil) 430 | (no-chg t) 431 | (t (buffer-string))))) 432 | (list output error-output))))))))) 433 | 434 | (defun format-all--buffer-native (mode &rest funcs) 435 | "Internal helper function to implement formatters. 436 | 437 | In a new temp buffer, switches to MODE then calls FUNCS in order 438 | to format the code. MODE and FUNCS should be symbols instead of 439 | functions to avoid warnings from the Emacs byte compiler." 440 | (format-all--buffer-thunk 441 | (lambda (input) 442 | (funcall mode) 443 | (insert input) 444 | (mapc #'funcall funcs) 445 | (format-all--fix-trailing-whitespace) 446 | (list nil "")))) 447 | 448 | (defun format-all--locate-file (filename) 449 | "Internal helper to locate dominating copy of FILENAME for current buffer." 450 | (let* ((dir (and (buffer-file-name) 451 | (locate-dominating-file (buffer-file-name) filename)))) 452 | (when dir (expand-file-name (concat dir filename))))) 453 | 454 | (defun format-all--locate-default-directory (root-files) 455 | "Internal helper function to find working directory for formatter. 456 | 457 | ROOT-FILES is a list of strings which are the filenames to look 458 | for using `locate-dominating-file'. Details in documentation for 459 | `format-all--buffer-hard'." 460 | (let ((found-dirs 461 | (when (and root-files (buffer-file-name)) 462 | (mapcan (lambda (root-file) 463 | (let ((found-file (locate-dominating-file 464 | (buffer-file-name) root-file))) 465 | (when found-file 466 | (list (file-name-directory found-file))))) 467 | root-files)))) 468 | (or (car (sort found-dirs (lambda (a b) (> (length a) (length b))))) 469 | (and (buffer-file-name) (file-name-directory (buffer-file-name))) 470 | default-directory))) 471 | 472 | (defun format-all--buffer-hard 473 | (ok-statuses error-regexp root-files executable &rest args) 474 | "Internal helper function to implement formatters. 475 | 476 | Runs the external program EXECUTABLE. The program shall read 477 | unformatted code from stdin, write its formatted equivalent to 478 | stdout, and write errors/warnings to stderr. 479 | 480 | The program should exit with status zero for the formatting to be 481 | considered successful. If a list of OK-STATUSES is given, all of 482 | those are actually considered successful. But if ERROR-REGEXP is 483 | given, and the program's stderr contains that regexp, then the 484 | formatting is considered failed even if the exit status is in 485 | OK-STATUSES. OK-STATUSES and ERROR-REGEXP are hacks to work 486 | around formatter programs that don't make sensible use of their 487 | exit status. 488 | 489 | If ARGS are given, those are arguments to EXECUTABLE. They should 490 | not be shell-quoted. 491 | 492 | If ROOT-FILES are given, the working directory of the formatter 493 | will be the deepest directory (starting from the file being 494 | formatted) containing one of these files. If ROOT-FILES is nil, 495 | or none of ROOT-FILES are found in any parent directories, the 496 | working directory will be the one where the formatted file is. 497 | ROOT-FILES is ignored for buffers that are not visiting a file." 498 | (let ((ok-statuses (or ok-statuses '(0))) 499 | (args (append format-all--user-args (format-all--flatten-once args))) 500 | (default-directory (format-all--locate-default-directory root-files))) 501 | (when format-all-debug 502 | (message "Format-All: Running: %s" 503 | (mapconcat #'shell-quote-argument (cons executable args) " ")) 504 | (message "Format-All: Directory: %s" default-directory)) 505 | (format-all--buffer-thunk 506 | (lambda (input) 507 | (let* ((errfile (make-temp-file "format-all-")) 508 | (status (apply #'call-process-region input nil 509 | executable nil (list t errfile) 510 | nil args)) 511 | (error-output (with-temp-buffer 512 | (insert-file-contents errfile) 513 | (delete-file errfile) 514 | (buffer-string))) 515 | (errorp (or (not (member status ok-statuses)) 516 | (and error-regexp 517 | (save-match-data 518 | (string-match error-regexp error-output)))))) 519 | (list errorp error-output)))))) 520 | 521 | (defun format-all--buffer-easy (executable &rest args) 522 | "Internal helper function to implement formatters. 523 | 524 | Runs the external program EXECUTABLE. The program shall read 525 | unformatted code from stdin, write its formatted equivalent to 526 | stdout, write errors/warnings to stderr, and exit zero/non-zero 527 | on success/failure. 528 | 529 | If ARGS are given, those are arguments to EXECUTABLE. They don't 530 | need to be shell-quoted." 531 | (apply 'format-all--buffer-hard nil nil nil executable args)) 532 | 533 | (defun format-all--ruby-gem-bundled-p (gem-name) 534 | "Internal helper function to check for a Ruby gem. 535 | 536 | Returns t if GEM-NAME is listed in the current project's 537 | Gemfile.lock, nil otherwise." 538 | (let* ((file (buffer-file-name)) 539 | (lockfile "Gemfile.lock") 540 | (lockdir (and file (locate-dominating-file file lockfile))) 541 | (lockfile (and lockdir (expand-file-name lockfile lockdir)))) 542 | (and lockfile 543 | (with-temp-buffer 544 | (insert-file-contents lockfile) 545 | (re-search-forward (format "^ %s " (regexp-quote gem-name)) 546 | nil t)) 547 | t))) 548 | 549 | (defun format-all--buffer-hard-ruby 550 | (gem-name ok-statuses error-regexp root-files executable &rest args) 551 | "Internal helper function to implement ruby based formatters. 552 | 553 | GEM-NAME is the name of a Ruby gem required to run EXECUTABLE. 554 | 555 | For OK-STATUSES, ERROR-REGEXP, ROOT-FILES, EXECUTABLE and ARGS, 556 | see `format-all--buffer-hard'." 557 | (let* ((command (file-name-nondirectory executable)) 558 | (error-regexp 559 | (regexp-opt 560 | (append 561 | (if error-regexp (list error-regexp)) 562 | (list 563 | "Bundler::GemNotFound" 564 | (concat "bundler: failed to load command: " 565 | (regexp-quote command)) 566 | (concat (regexp-opt (list "bundle" (regexp-quote command))) 567 | ": command not found"))))) 568 | (command-args 569 | (append 570 | (if (format-all--ruby-gem-bundled-p gem-name) 571 | (list "bundle" "exec" command) 572 | (list executable)) 573 | (format-all--flatten-once args)))) 574 | (format-all--buffer-hard 575 | ok-statuses error-regexp root-files 576 | (car command-args) 577 | (cdr command-args)))) 578 | 579 | (defvar format-all--executable-table (make-hash-table) 580 | "Internal table of formatter executable names for format-all.") 581 | 582 | (defvar format-all--install-table (make-hash-table) 583 | "Internal table of formatter install commands for format-all.") 584 | 585 | (defvar format-all--language-table (make-hash-table :test 'equal) 586 | "Internal table of major mode formatter lists for format-all.") 587 | 588 | (defvar format-all--features-table (make-hash-table) 589 | "Internal table of formatter feature lists for format-all.") 590 | 591 | (defvar format-all--format-table (make-hash-table) 592 | "Internal table of formatter formatting functions for format-all.") 593 | 594 | (defun format-all--pushhash (key value table) 595 | "Push VALUE onto the list under KEY in hash table TABLE." 596 | (puthash key (cons value (remove value (gethash key table))) table)) 597 | 598 | (defmacro define-format-all-formatter (formatter &rest body) 599 | "Define a new source code formatter for use with format-all. 600 | 601 | FORMATTER is a symbol naming the formatter. The name of the 602 | command used to run the formatter is usually a good choice. 603 | 604 | Consult the existing formatters for examples of BODY." 605 | (declare (indent 1)) 606 | (let (executable install languages features format) 607 | (cl-assert 608 | (equal (mapcar 'car body) 609 | '(:executable :install :languages :features :format))) 610 | (cl-dolist (part body) 611 | (cl-ecase (car part) 612 | (:executable 613 | (setq executable 614 | (unless (null (cdr part)) 615 | (or (format-all--resolve-system (cdr part)) 616 | (error "Executable not specified for %S system %S" 617 | formatter format-all--system-type))))) 618 | (:install 619 | (setq install (format-all--resolve-system (cdr part)))) 620 | (:languages 621 | (setq languages 622 | (mapcar (lambda (language) 623 | `(format-all--pushhash 624 | ',language ',formatter format-all--language-table)) 625 | (cdr part)))) 626 | (:features 627 | (setq features (cdr part))) 628 | (:format 629 | (setq format `(lambda (executable language region) 630 | (ignore language 631 | ,@(unless executable '(executable)) 632 | ,@(unless (memq 'region features) '(region))) 633 | ,(cadr part)))))) 634 | `(progn (puthash ',formatter ,executable format-all--executable-table) 635 | (puthash ',formatter ,install format-all--install-table) 636 | ,@languages 637 | (puthash ',formatter ',features format-all--features-table) 638 | (puthash ',formatter ,format format-all--format-table) 639 | ',formatter))) 640 | 641 | (define-format-all-formatter alejandra 642 | (:executable "alejandra") 643 | (:install "nix-env -if https://github.com/kamadorueda/alejandra/tarball/master") 644 | (:languages "Nix") 645 | (:features) 646 | (:format (format-all--buffer-easy executable))) 647 | 648 | (define-format-all-formatter asmfmt 649 | (:executable "asmfmt") 650 | (:install) 651 | (:languages "Assembly") 652 | (:features) 653 | (:format (format-all--buffer-easy executable))) 654 | 655 | (define-format-all-formatter astyle 656 | (:executable "astyle") 657 | (:install (macos "brew install astyle")) 658 | (:languages "C" "C++" "C#" "Java") 659 | (:features) 660 | (:format (format-all--buffer-easy 661 | executable 662 | (let ((astylerc (format-all--locate-file ".astylerc"))) 663 | (when astylerc (concat "--options=" astylerc)))))) 664 | 665 | (define-format-all-formatter atsfmt 666 | (:executable "atsfmt") 667 | (:install "cabal new-install ats-format --happy-options='-gcsa' -O2") 668 | (:languages "ATS") 669 | (:features) 670 | (:format (format-all--buffer-easy executable))) 671 | 672 | (define-format-all-formatter auctex 673 | (:executable) 674 | (:install) 675 | (:languages "LaTeX") 676 | (:features) 677 | (:format (format-all--buffer-native 678 | 'latex-mode 679 | (lambda () 680 | (let ((f (symbol-function 'LaTeX-fill-buffer))) 681 | (when f (funcall f nil))))))) 682 | 683 | (define-format-all-formatter bean-format 684 | (:executable "bean-format") 685 | (:install "pip install beancount") 686 | (:languages "_Beancount") 687 | (:features) 688 | (:format (format-all--buffer-easy executable))) 689 | 690 | (define-format-all-formatter beautysh 691 | (:executable "beautysh") 692 | (:install "pip install beautysh") 693 | (:languages "Shell") 694 | (:features) 695 | (:format (format-all--buffer-easy executable "-"))) 696 | 697 | (define-format-all-formatter black 698 | (:executable "black") 699 | (:install "pip install black") 700 | (:languages "Python") 701 | (:features) 702 | (:format (format-all--buffer-easy 703 | executable "-q" 704 | (when (format-all--buffer-extension-p "pyi") "--pyi") 705 | "-"))) 706 | 707 | (define-format-all-formatter brittany 708 | (:executable "brittany") 709 | (:install "stack install brittany") 710 | (:languages "Haskell" "Literate Haskell") 711 | (:features) 712 | (:format (format-all--buffer-easy executable))) 713 | 714 | (define-format-all-formatter bsrefmt 715 | (:executable "bsrefmt") 716 | (:install "npm install --global bs-platform") 717 | (:languages "Reason") 718 | (:features) 719 | (:format (format-all--buffer-easy executable))) 720 | 721 | (define-format-all-formatter buildifier 722 | (:executable "buildifier") 723 | (:install 724 | (macos "brew install buildifier") 725 | "go get github.com/bazelbuild/buildtools/buildifier") 726 | (:languages "Bazel") 727 | (:features) 728 | (:format (format-all--buffer-easy executable))) 729 | 730 | (define-format-all-formatter cabal-fmt 731 | (:executable "cabal-fmt") 732 | (:install "cabal install cabal-fmt") 733 | (:languages "Cabal Config") 734 | (:features) 735 | (:format (format-all--buffer-easy executable))) 736 | 737 | (define-format-all-formatter caddy-fmt 738 | (:executable "caddy") 739 | (:install 740 | (macos "brew install caddy") 741 | (windows "scoop install caddy")) 742 | (:languages "_Caddyfile") 743 | (:features) 744 | (:format (format-all--buffer-easy executable "fmt" "-"))) 745 | 746 | (define-format-all-formatter cargo-fmt 747 | ;; This is the same formatter as rustfmt, but run via `cargo fmt`. 748 | (:executable "cargo") 749 | (:install "rustup component add rustfmt") 750 | (:languages "Rust") 751 | (:features) 752 | (:format (format-all--buffer-easy executable "fmt"))) 753 | 754 | (define-format-all-formatter clang-format 755 | (:executable "clang-format") 756 | (:install 757 | (macos "brew install clang-format") 758 | (windows "scoop install llvm")) 759 | (:languages 760 | "_AZSL" "C" "C#" "C++" "Cuda" "GLSL" "HLSL" "Java" "Objective-C" "Protocol Buffer") 761 | (:features region) 762 | (:format 763 | (format-all--buffer-easy 764 | executable 765 | "-assume-filename" 766 | (or (buffer-file-name) 767 | (cdr (assoc language 768 | '(("C" . ".c") 769 | ("C#" . ".cs") 770 | ("C++" . ".cpp") 771 | ("Cuda" . ".cu") 772 | ("GLSL" . ".glsl") 773 | ("Java" . ".java") 774 | ("Objective-C" . ".m") 775 | ("Protocol Buffer" . ".proto"))))) 776 | (when region 777 | (list "--offset" (number-to-string (1- (car region))) 778 | "--length" (number-to-string (- (cdr region) (car region)))))))) 779 | 780 | (define-format-all-formatter cljfmt 781 | (:executable "cljfmt") 782 | (:install) 783 | (:languages "Clojure") 784 | (:features) 785 | (:format (format-all--buffer-easy executable "fix" "-"))) 786 | 787 | (define-format-all-formatter cmake-format 788 | (:executable "cmake-format") 789 | (:install "pip install cmake-format") 790 | (:languages "CMake") 791 | (:features) 792 | (:format (format-all--buffer-easy executable "-"))) 793 | 794 | (define-format-all-formatter crystal 795 | (:executable "crystal") 796 | (:install (macos "brew install crystal")) 797 | (:languages "Crystal") 798 | (:features) 799 | (:format (format-all--buffer-easy executable "tool" "format" "-"))) 800 | 801 | (define-format-all-formatter csharpier 802 | (:executable "dotnet-csharpier") 803 | (:install "dotnet install -g csharpier") 804 | (:languages "C#") 805 | (:features) 806 | (:format (format-all--buffer-easy executable "--write-stdout"))) 807 | 808 | (define-format-all-formatter dart-format 809 | (:executable "dart") 810 | (:install (macos "brew tap dart-lang/dart && brew install dart")) 811 | (:languages "Dart") 812 | (:features) 813 | (:format 814 | (format-all--buffer-easy executable "format" "--output" "show"))) 815 | 816 | (define-format-all-formatter dartfmt 817 | (:executable "dartfmt") 818 | (:install (macos "brew tap dart-lang/dart && brew install dart")) 819 | (:languages "Dart") 820 | (:features) 821 | (:format 822 | (format-all--buffer-easy 823 | executable 824 | (when (buffer-file-name) 825 | (list "--stdin-name" (buffer-file-name)))))) 826 | 827 | (define-format-all-formatter deno 828 | (:executable "deno") 829 | (:install (macos "brew install deno")) 830 | (:languages 831 | "JavaScript" "JSX" 832 | "TypeScript" "TSX" 833 | "JSON" "JSON5" 834 | "Markdown") 835 | (:features) 836 | (:format 837 | (format-all--buffer-easy 838 | executable 839 | "fmt" 840 | "--ext" (let ((pair (assoc language 841 | '(("JavaScript" . "js") 842 | ("TypeScript" . "ts") 843 | ("JSON5" . "jsonc") 844 | ("Markdown" . "md"))))) 845 | (if pair (cdr pair) (downcase language))) 846 | "-"))) 847 | 848 | (define-format-all-formatter dfmt 849 | (:executable "dfmt") 850 | (:install (macos "brew install dfmt")) 851 | (:languages "D") 852 | (:features) 853 | (:format 854 | (format-all--buffer-hard nil (regexp-quote "[error]") nil executable))) 855 | 856 | (define-format-all-formatter dhall 857 | (:executable "dhall") 858 | (:install (macos "brew install dhall")) 859 | (:languages "Dhall") 860 | (:features) 861 | (:format (format-all--buffer-easy executable "format"))) 862 | 863 | (define-format-all-formatter dockfmt 864 | (:executable "dockfmt") 865 | (:install "go get github.com/jessfraz/dockfmt") 866 | (:languages "Dockerfile") 867 | (:features) 868 | (:format (format-all--buffer-easy executable "fmt"))) 869 | 870 | (define-format-all-formatter efmt 871 | (:executable "efmt") 872 | (:install "cargo install efmt") 873 | (:languages "Erlang") 874 | (:features) 875 | (:format (format-all--buffer-easy executable "-"))) 876 | 877 | (define-format-all-formatter elm-format 878 | (:executable "elm-format") 879 | (:install (macos "brew install elm")) 880 | (:languages "Elm") 881 | (:features) 882 | (:format 883 | (cl-destructuring-bind (output error-output) 884 | (format-all--buffer-hard nil nil '("elm.json" "elm-package.json") 885 | executable "--yes" "--stdin") 886 | (let ((error-output (format-all--remove-ansi-color error-output))) 887 | (list output error-output))))) 888 | 889 | (define-format-all-formatter emacs-bibtex 890 | (:executable) 891 | (:install) 892 | (:languages "BibTeX") 893 | (:features) 894 | (:format (format-all--buffer-native 'bibtex-mode 'bibtex-reformat))) 895 | 896 | (define-format-all-formatter emacs-bibtex-sort 897 | (:executable) 898 | (:install) 899 | (:languages "BibTeX") 900 | (:features) 901 | (:format (format-all--buffer-native 'bibtex-mode 'bibtex-sort-buffer))) 902 | 903 | (define-format-all-formatter emacs-hy 904 | (:executable) 905 | (:install) 906 | (:languages "Hy") 907 | (:features region) 908 | (:format 909 | (format-all--buffer-native 910 | 'hy-mode 911 | (if region 912 | (lambda () (indent-region (car region) (cdr region))) 913 | (lambda () (indent-region (point-min) (point-max))))))) 914 | 915 | (define-format-all-formatter emacs-lisp 916 | (:executable) 917 | (:install) 918 | (:languages "Emacs Lisp") 919 | (:features region) 920 | (:format 921 | (format-all--buffer-native 922 | 'emacs-lisp-mode 923 | (if region 924 | (lambda () (indent-region (car region) (cdr region))) 925 | (lambda () (indent-region (point-min) (point-max))))))) 926 | 927 | (define-format-all-formatter erb-format 928 | (:executable "erb-format") 929 | (:install "gem install erb-formatter") 930 | (:languages "HTML+ERB") 931 | (:features) 932 | (:format (format-all--buffer-easy executable "--stdin"))) 933 | 934 | (define-format-all-formatter fantomas 935 | (:executable "fantomas") 936 | (:install "dotnet tool install -g fantomas-tool") 937 | (:languages "F#") 938 | (:features) 939 | (:format (format-all--buffer-easy executable "--stdin" "--stdout"))) 940 | 941 | (define-format-all-formatter fish-indent 942 | (:executable "fish_indent") 943 | (:install (macos "brew install fish OR port install fish")) 944 | (:languages "Fish") 945 | (:features) 946 | (:format (format-all--buffer-easy executable))) 947 | 948 | (define-format-all-formatter fourmolu 949 | (:executable "fourmolu") 950 | (:install "stack install fourmolu") 951 | (:languages "Haskell" "Literate Haskell") 952 | (:features) 953 | (:format 954 | (format-all--buffer-easy 955 | executable 956 | (when (buffer-file-name) 957 | (list "--stdin-input-file" (buffer-file-name)))))) 958 | 959 | (define-format-all-formatter fprettify 960 | (:executable "fprettify") 961 | (:install "pip install fprettify") 962 | (:languages "Fortran Free Form") 963 | (:features) 964 | (:format (format-all--buffer-easy executable "--silent"))) 965 | 966 | (define-format-all-formatter gawk 967 | (:executable "gawk") 968 | (:install (macos "brew install gawk")) 969 | (:languages "Awk") 970 | (:features) 971 | (:format (format-all--buffer-easy executable "-f" "-" "--pretty-print=-"))) 972 | 973 | (define-format-all-formatter gleam 974 | (:executable "gleam") 975 | (:install (macos "brew install gleam")) 976 | (:languages "_Gleam") 977 | (:features) 978 | (:format (format-all--buffer-easy executable "format" "--stdin"))) 979 | 980 | (define-format-all-formatter gofmt 981 | (:executable "gofmt") 982 | (:install 983 | (macos "brew install go") 984 | (windows "scoop install go")) 985 | (:languages "Go") 986 | (:features) 987 | (:format (format-all--buffer-easy executable))) 988 | 989 | (define-format-all-formatter goimports 990 | (:executable "goimports") 991 | (:install "go get golang.org/x/tools/cmd/goimports") 992 | (:languages "Go") 993 | (:features) 994 | (:format (format-all--buffer-easy executable))) 995 | 996 | (define-format-all-formatter google-java-format 997 | (:executable "google-java-format") 998 | (:install) 999 | (:languages "Java") 1000 | (:features) 1001 | (:format (format-all--buffer-easy executable "-"))) 1002 | 1003 | (define-format-all-formatter hclfmt 1004 | (:executable "hclfmt") 1005 | (:install "go install github.com/hashicorp/hcl/v2/cmd/hclfmt@latest") 1006 | (:languages "HCL") 1007 | (:features) 1008 | (:format (format-all--buffer-easy executable))) 1009 | 1010 | (define-format-all-formatter hindent 1011 | (:executable "hindent") 1012 | (:install "stack install hindent") 1013 | (:languages "Haskell" "Literate Haskell") 1014 | (:features) 1015 | (:format (format-all--buffer-easy executable))) 1016 | 1017 | (define-format-all-formatter html-tidy 1018 | (:executable "tidy") 1019 | (:install 1020 | (macos "brew install tidy-html5") 1021 | (windows "scoop install tidy")) 1022 | (:languages "HTML" "XML") 1023 | (:features) 1024 | (:format 1025 | (format-all--buffer-hard 1026 | '(0 1) nil nil 1027 | executable 1028 | "-q" 1029 | "--tidy-mark" "no" 1030 | "-indent" 1031 | (when (equal language "XML") "-xml")))) 1032 | 1033 | (define-format-all-formatter isort 1034 | (:executable "isort") 1035 | (:install "pip install isort") 1036 | (:languages "Python") 1037 | (:features) 1038 | (:format (format-all--buffer-easy executable "-q" "-"))) 1039 | 1040 | (define-format-all-formatter istyle-verilog 1041 | (:executable "iStyle") 1042 | (:install) 1043 | (:languages "Verilog") 1044 | (:features) 1045 | (:format (format-all--buffer-easy executable))) 1046 | 1047 | (define-format-all-formatter jsonnetfmt 1048 | (:executable "jsonnetfmt") 1049 | (:install (macos "brew install jsonnet")) 1050 | (:languages "Jsonnet") 1051 | (:features) 1052 | (:format (format-all--buffer-easy executable "-"))) 1053 | 1054 | (define-format-all-formatter ktlint 1055 | (:executable "ktlint") 1056 | (:install (macos "brew install ktlint")) 1057 | (:languages "Kotlin") 1058 | (:features) 1059 | (:format (format-all--buffer-easy executable "--log-level=none" "--format" "--stdin"))) 1060 | 1061 | (define-format-all-formatter latexindent 1062 | (:executable "latexindent") 1063 | (:install) 1064 | (:languages "LaTeX") 1065 | (:features) 1066 | (:format (format-all--buffer-easy executable))) 1067 | 1068 | (define-format-all-formatter ledger-mode 1069 | (:executable) 1070 | (:install) 1071 | (:languages "_Ledger") 1072 | (:features) 1073 | (:format 1074 | (format-all--buffer-native 'ledger-mode 'ledger-mode-clean-buffer))) 1075 | 1076 | (define-format-all-formatter lua-fmt 1077 | (:executable "luafmt") 1078 | (:install "npm install --global lua-fmt") 1079 | (:languages "Lua") 1080 | (:features) 1081 | (:format (format-all--buffer-easy executable "--stdin"))) 1082 | 1083 | (define-format-all-formatter mix-format 1084 | (:executable "mix") 1085 | (:install (macos "brew install elixir")) 1086 | (:languages "Elixir" "HTML+EEX") 1087 | (:features) 1088 | (:format 1089 | (format-all--buffer-hard 1090 | nil nil '("mix.exs") 1091 | executable 1092 | "format" 1093 | (let ((config-file (format-all--locate-file ".formatter.exs"))) 1094 | (when config-file (list "--dot-formatter" config-file))) 1095 | (cond ((buffer-file-name) 1096 | (list "--stdin-filename" (buffer-file-name))) 1097 | ((equal language "HTML+EEX") 1098 | (list "--stdin-filename" "stdin.heex")) 1099 | (t 1100 | (list))) 1101 | "-"))) 1102 | 1103 | (define-format-all-formatter muon-fmt 1104 | (:executable "muon") 1105 | (:install (macos "brew install muon")) 1106 | (:languages "Meson") 1107 | (:features) 1108 | (:format (format-all--buffer-easy executable "fmt" "-"))) 1109 | 1110 | (define-format-all-formatter nginxfmt 1111 | (:executable "nginxfmt") 1112 | (:install "pip install nginxfmt") 1113 | (:languages "_Nginx") 1114 | (:features) 1115 | (:format (format-all--buffer-easy executable "-"))) 1116 | 1117 | (define-format-all-formatter nixfmt 1118 | (:executable "nixfmt") 1119 | (:install 1120 | "nix-env -f https://github.com/serokell/nixfmt/archive/master.tar.gz -i") 1121 | (:languages "Nix") 1122 | (:features) 1123 | (:format (format-all--buffer-easy executable))) 1124 | 1125 | (define-format-all-formatter nixpkgs-fmt 1126 | (:executable "nixpkgs-fmt") 1127 | (:install "nix-env -f https://github.com/nix-community/nixpkgs-fmt/archive/master.tar.gz -i") 1128 | (:languages "Nix") 1129 | (:features) 1130 | (:format (format-all--buffer-easy executable))) 1131 | 1132 | (define-format-all-formatter ocamlformat 1133 | (:executable "ocamlformat") 1134 | (:install "opam install ocamlformat") 1135 | (:languages "OCaml") 1136 | (:features) 1137 | (:format 1138 | (format-all--buffer-easy 1139 | executable "-" 1140 | (when (buffer-file-name) (concat "--name=" (buffer-file-name)))))) 1141 | 1142 | (define-format-all-formatter ocp-indent 1143 | (:executable "ocp-indent") 1144 | (:install "opam install ocp-indent") 1145 | (:languages "OCaml") 1146 | (:features) 1147 | (:format (format-all--buffer-easy executable))) 1148 | 1149 | (define-format-all-formatter ormolu 1150 | (:executable "ormolu") 1151 | (:install "stack install ormolu") 1152 | (:languages "Haskell" "Literate Haskell") 1153 | (:features) 1154 | (:format 1155 | (format-all--buffer-easy 1156 | executable 1157 | (when (buffer-file-name) 1158 | (list "--stdin-input-file" (buffer-file-name)))))) 1159 | 1160 | (define-format-all-formatter perltidy 1161 | (:executable "perltidy") 1162 | (:install "cpan install Perl::Tidy") 1163 | (:languages "Perl") 1164 | (:features region) 1165 | (:format 1166 | (format-all--buffer-easy 1167 | executable 1168 | "--standard-error-output" 1169 | (when region 1170 | (format "--line-range-tidy=%d:%d" 1171 | (line-number-at-pos (car region)) 1172 | (line-number-at-pos (cdr region))))))) 1173 | 1174 | (define-format-all-formatter pgformatter 1175 | (:executable "pg_format") 1176 | (:install) 1177 | (:languages "SQL") 1178 | (:features) 1179 | (:format (format-all--buffer-easy executable))) 1180 | 1181 | (define-format-all-formatter prettier 1182 | (:executable "prettier") 1183 | (:install "npm install --global prettier @prettier/plugin-lua @prettier/plugin-php prettier-plugin-solidity prettier-plugin-svelte prettier-plugin-toml") 1184 | (:languages 1185 | "CSS" "GraphQL" "HTML" "JavaScript" "JSON" "JSON5" "JSX" "Less" "Lua" 1186 | "Markdown" "PHP" "SCSS" "Solidity" "Svelte" "TOML" "TSX" "TypeScript" 1187 | "Vue" "YAML" 1188 | "_Angular" "_Flow") 1189 | (:features region) 1190 | (:format 1191 | (format-all--buffer-easy 1192 | executable 1193 | (when (let* ((file (buffer-file-name)) 1194 | (info (and file 1195 | (with-temp-buffer 1196 | (call-process executable nil t nil 1197 | "--file-info" file) 1198 | (buffer-string))))) 1199 | (when (and format-all-debug info) 1200 | (message "Format-All: --file-info: %s" info)) 1201 | (or (not info) 1202 | (save-match-data 1203 | (string-match 1204 | (regexp-quote "\"inferredParser\": null") 1205 | info)))) 1206 | (list "--parser" 1207 | (let ((pair (assoc language 1208 | '(("_Angular" . "angular") 1209 | ("_Flow" . "flow") 1210 | ("JavaScript" . "babel") 1211 | ("JSX" . "babel") 1212 | ("Solidity" . "solidity-parse") 1213 | ("TSX" . "typescript"))))) 1214 | (if pair (cdr pair) (downcase language))))) 1215 | (when (buffer-file-name) 1216 | (list "--stdin-filepath" (buffer-file-name))) 1217 | (let ((ignore-file (format-all--locate-file ".prettierignore"))) 1218 | (when ignore-file 1219 | (list "--ignore-path" ignore-file))) 1220 | (when region 1221 | (list "--range-start" (number-to-string (1- (car region))) 1222 | "--range-end" (number-to-string (1- (cdr region)))))))) 1223 | 1224 | (define-format-all-formatter prettierd 1225 | (:executable "prettierd") 1226 | (:install "npm install --global @fsouza/prettierd") 1227 | (:languages 1228 | "CSS" "GraphQL" "HTML" "JavaScript" "JSON" "JSON5" "JSX" 1229 | "Less" "Markdown" "SCSS" "TSX" "TypeScript" "Vue" "YAML") 1230 | (:features) 1231 | (:format 1232 | (format-all--buffer-easy 1233 | executable 1234 | (or (buffer-file-name) 1235 | (buffer-name))))) 1236 | 1237 | (define-format-all-formatter purs-tidy 1238 | (:executable "purs-tidy") 1239 | (:install "npm install --global purs-tidy") 1240 | (:languages "PureScript") 1241 | (:features) 1242 | (:format (format-all--buffer-easy executable "format"))) 1243 | 1244 | (define-format-all-formatter purty 1245 | (:executable "purty") 1246 | (:install "npm install --global purty") 1247 | (:languages "PureScript") 1248 | (:features) 1249 | (:format (format-all--buffer-easy executable "-"))) 1250 | 1251 | (define-format-all-formatter raco-fmt 1252 | (:executable "raco") 1253 | (:install "raco pkg install fmt") 1254 | (:languages "Racket") 1255 | (:features) 1256 | (:format (format-all--buffer-easy executable "fmt"))) 1257 | 1258 | (define-format-all-formatter rescript 1259 | (:executable "rescript") 1260 | (:install "npm install --global rescript") 1261 | (:languages "ReScript") 1262 | (:features) 1263 | (:format 1264 | (format-all--buffer-easy 1265 | executable "format" "-stdin" 1266 | (let ((ext (if (not (buffer-file-name)) "" 1267 | (file-name-extension (buffer-file-name))))) 1268 | (concat "." (if (equal ext "") "res" ext)))))) 1269 | 1270 | (define-format-all-formatter rubocop 1271 | (:executable "rubocop") 1272 | (:install "gem install rubocop:'>=1.4.0'") 1273 | (:languages "Ruby") 1274 | (:features) 1275 | (:format 1276 | (format-all--buffer-hard-ruby 1277 | "rubocop" '(0 1) nil nil 1278 | executable 1279 | "--auto-correct" 1280 | "--format" "quiet" 1281 | "--stderr" 1282 | "--stdin" (or (buffer-file-name) (buffer-name))))) 1283 | 1284 | (define-format-all-formatter ruff 1285 | (:executable "ruff") 1286 | (:install "pip install ruff") 1287 | (:languages "Python") 1288 | (:features) 1289 | (:format (format-all--buffer-easy 1290 | executable "format" 1291 | "--silent" 1292 | "--stdin-filename" (or (buffer-file-name) (buffer-name)) 1293 | "-"))) 1294 | 1295 | (define-format-all-formatter rufo 1296 | (:executable "rufo") 1297 | (:install "gem install rufo") 1298 | (:languages "Ruby") 1299 | (:features) 1300 | (:format 1301 | (format-all--buffer-hard-ruby 1302 | "rufo" nil nil nil 1303 | executable 1304 | "--simple-exit" 1305 | (when (buffer-file-name) 1306 | (list "--filename" (buffer-file-name)))))) 1307 | 1308 | (define-format-all-formatter rustfmt 1309 | (:executable "rustfmt") 1310 | (:install "rustup component add rustfmt") 1311 | (:languages "Rust") 1312 | (:features) 1313 | (:format (format-all--buffer-easy executable))) 1314 | 1315 | (define-format-all-formatter scalafmt 1316 | (:executable "scalafmt") 1317 | (:install "coursier bootstrap org.scalameta:scalafmt-cli_2.12:2.4.0-RC1 -r sonatype:snapshots -o /usr/local/bin/scalafmt --standalone --main org.scalafmt.cli.Cli") 1318 | (:languages "Scala") 1319 | (:features) 1320 | (:format 1321 | (format-all--buffer-easy 1322 | executable "--stdin" "--non-interactive" "--quiet" "--stdout"))) 1323 | 1324 | (define-format-all-formatter shfmt 1325 | (:executable "shfmt") 1326 | (:install 1327 | (macos "brew install shfmt") 1328 | (windows "scoop install shfmt")) 1329 | (:languages "Shell") 1330 | (:features) 1331 | (:format 1332 | (format-all--buffer-easy 1333 | executable 1334 | (if (buffer-file-name) 1335 | (list "-filename" (buffer-file-name)) 1336 | (list "-ln" (cl-case (and (eql major-mode 'sh-mode) 1337 | (boundp 'sh-shell) 1338 | (symbol-value 'sh-shell)) 1339 | (bash "bash") 1340 | (mksh "mksh") 1341 | (t "posix"))))))) 1342 | 1343 | (define-format-all-formatter snakefmt 1344 | (:executable "snakefmt") 1345 | (:install) 1346 | (:languages "_Snakemake") 1347 | (:features) 1348 | (:format (format-all--buffer-easy executable "-"))) 1349 | 1350 | (define-format-all-formatter sqlformat 1351 | (:executable "sqlformat") 1352 | (:install "pip install sqlparse") 1353 | (:languages "SQL") 1354 | (:features) 1355 | (:format 1356 | (let* ((ic (car default-process-coding-system)) 1357 | (oc (cdr default-process-coding-system)) 1358 | (ienc (symbol-name (or (coding-system-get ic :mime-charset) 1359 | 'utf-8))) 1360 | (oenc (symbol-name (or (coding-system-get oc :mime-charset) 1361 | 'utf-8))) 1362 | (process-environment (cons (concat "PYTHONIOENCODING=" oenc) 1363 | process-environment))) 1364 | (format-all--buffer-easy executable "--encoding" ienc "-")))) 1365 | 1366 | (define-format-all-formatter standard 1367 | (:executable "standard") 1368 | (:install "npm install --global standard") 1369 | (:languages "JavaScript" "JSX") 1370 | (:features) 1371 | (:format 1372 | ;; `standard --stdin` properly uses zero vs non-zero exit codes to 1373 | ;; indicate success vs error. However, it checks for quite a broad 1374 | ;; range of errors, all the way up to undeclared identifiers and 1375 | ;; such. To catch only syntax errors, we need to look specifically 1376 | ;; for the text "Parsing error:". 1377 | (format-all--buffer-hard 1378 | '(0 1) ".*?:.*?:[0-9]+:[0-9]+: Parsing error:" nil 1379 | executable "--fix" "--stdin"))) 1380 | 1381 | (define-format-all-formatter standardrb 1382 | (:executable "standardrb") 1383 | (:install "gem install standard:'>=0.13.0'") 1384 | (:languages "Ruby") 1385 | (:features) 1386 | (:format 1387 | (format-all--buffer-hard-ruby 1388 | "standard" '(0 1) nil nil 1389 | executable 1390 | "--stderr" 1391 | "--fix" 1392 | "--stdin" (or (buffer-file-name) (buffer-name))))) 1393 | 1394 | (define-format-all-formatter stree 1395 | (:executable "stree") 1396 | (:install "gem install syntax_tree:'>=2.0.1'") 1397 | (:languages "Ruby") 1398 | (:features) 1399 | (:format 1400 | (format-all--buffer-hard-ruby 1401 | "stree" '(0 1) nil '(".streerc") 1402 | executable 1403 | "format"))) 1404 | 1405 | (define-format-all-formatter styler 1406 | (:executable "Rscript") 1407 | (:install "Rscript -e \"install.packages('styler')\"") 1408 | (:languages "R") 1409 | (:features) 1410 | (:format 1411 | (format-all--buffer-easy 1412 | executable 1413 | "-e" (concat 1414 | "options(styler.colored_print.vertical=FALSE);" 1415 | " con <- file('stdin');" 1416 | " out <- styler::style_text(readLines(con));" 1417 | " close(con);" 1418 | " out")))) 1419 | 1420 | (define-format-all-formatter stylish-haskell 1421 | (:executable "stylish-haskell") 1422 | (:install "stack install stylish-haskell") 1423 | (:languages "Haskell") 1424 | (:features) 1425 | (:format (format-all--buffer-easy executable))) 1426 | 1427 | (define-format-all-formatter stylua 1428 | (:executable "stylua") 1429 | (:install "cargo install stylua") 1430 | (:languages "Lua") 1431 | (:features) 1432 | (:format (format-all--buffer-easy executable "-"))) 1433 | 1434 | (define-format-all-formatter swiftformat 1435 | (:executable "swiftformat") 1436 | (:install (macos "brew install swiftformat")) 1437 | (:languages "Swift") 1438 | (:features region) 1439 | (:format 1440 | (format-all--buffer-easy 1441 | executable 1442 | "--quiet" 1443 | (let ((config (format-all--locate-file ".swiftformat"))) 1444 | (when config (list "--config" config))) 1445 | (when region 1446 | (list "--linerange" (format "%d,%d" 1447 | (line-number-at-pos (car region)) 1448 | (line-number-at-pos (cdr region)))))))) 1449 | 1450 | (define-format-all-formatter taplo-fmt 1451 | (:executable "taplo") 1452 | (:install "npm install --global @taplo/cli") 1453 | (:languages "TOML") 1454 | (:features) 1455 | (:format (format-all--buffer-easy executable "fmt" "-"))) 1456 | 1457 | (define-format-all-formatter terraform-fmt 1458 | (:executable "terraform") 1459 | (:install (macos "brew install terraform")) 1460 | (:languages "Terraform") 1461 | (:features) 1462 | (:format (format-all--buffer-easy executable "fmt" "-no-color" "-"))) 1463 | 1464 | (define-format-all-formatter ts-standard 1465 | (:executable "ts-standard") 1466 | (:install "npm install --global ts-standard") 1467 | (:languages "TypeScript" "TSX") 1468 | (:features) 1469 | (:format 1470 | ;; `ts-standard --stdin` properly uses zero vs non-zero exit codes to 1471 | ;; indicate success vs error. However, it checks for quite a broad 1472 | ;; range of errors, all the way up to undeclared identifiers and 1473 | ;; such. To catch only syntax errors, we need to look specifically 1474 | ;; for the text "Parsing error:". 1475 | (format-all--buffer-hard 1476 | '(0 1) ".*?:.*?:[0-9]+:[0-9]+: Parsing error:" '("tsconfig.json") 1477 | executable "--fix" "--stdin" 1478 | (when (buffer-file-name) 1479 | (list "--stdin-filename" (buffer-file-name)))))) 1480 | 1481 | (define-format-all-formatter v-fmt 1482 | (:executable "v") 1483 | (:install) 1484 | (:languages "V") 1485 | (:features) 1486 | (:format (format-all--buffer-easy executable "fmt"))) 1487 | 1488 | (define-format-all-formatter verible 1489 | (:executable "verible-verilog-format") 1490 | (:install) 1491 | (:languages "Verilog" "SystemVerilog") 1492 | (:features) 1493 | (:format (format-all--buffer-easy executable "-"))) 1494 | 1495 | (define-format-all-formatter yapf 1496 | (:executable "yapf") 1497 | (:install "pip install yapf") 1498 | (:languages "Python") 1499 | (:features) 1500 | (:format (format-all--buffer-easy executable))) 1501 | 1502 | (define-format-all-formatter zig 1503 | (:executable "zig") 1504 | (:install) 1505 | (:languages "Zig") 1506 | (:features) 1507 | (:format (format-all--buffer-easy executable "fmt" "--stdin"))) 1508 | 1509 | (define-format-all-formatter zprint 1510 | (:executable "zprint") 1511 | (:install) 1512 | (:languages "Clojure") 1513 | (:features) 1514 | (:format (format-all--buffer-easy executable))) 1515 | 1516 | (defun format-all--language-id-buffer () 1517 | "Return the language used in the current buffer, or NIL. 1518 | 1519 | Prefer getting the ID from the language-id library. Some 1520 | languages do not yet have official GitHub Linguist identifiers, 1521 | yet format-all needs to know about them anyway. That's why we 1522 | have this custom language-id function in format-all. The 1523 | unofficial languages IDs are prefixed with \"_\"." 1524 | (or (and (or (equal major-mode 'angular-html-mode) 1525 | (and (equal major-mode 'web-mode) 1526 | (equal (symbol-value 'web-mode-content-type) "html") 1527 | (equal (symbol-value 'web-mode-engine) "angular"))) 1528 | "_Angular") 1529 | (and (member major-mode '(js-mode js2-mode js3-mode)) 1530 | (boundp 'flow-minor-mode) 1531 | (not (null (symbol-value 'flow-minor-mode))) 1532 | "_Flow") 1533 | (and (equal major-mode 'azsl-mode) "_AZSL") 1534 | (and (equal major-mode 'beancount-mode) "_Beancount") 1535 | (and (equal major-mode 'caddyfile-mode) "_Caddyfile") 1536 | (and (equal major-mode 'gleam-mode) "_Gleam") 1537 | (and (equal major-mode 'ledger-mode) "_Ledger") 1538 | (and (equal major-mode 'nginx-mode) "_Nginx") 1539 | (and (equal major-mode 'snakemake-mode) "_Snakemake") 1540 | (language-id-buffer))) 1541 | 1542 | (defun format-all--please-install (executable installer) 1543 | "Internal helper function for error about missing EXECUTABLE and INSTALLER." 1544 | (concat (format "You need the %s command." executable) 1545 | (if (not installer) "" 1546 | (format " You may be able to install it via: %s" installer)))) 1547 | 1548 | (defun format-all--formatter-executable (formatter) 1549 | "Internal helper function to get the external program for FORMATTER." 1550 | (let ((executable (gethash formatter format-all--executable-table))) 1551 | (when executable 1552 | (or (executable-find executable) 1553 | (signal 'format-all-executable-not-found 1554 | (list (format-all--please-install 1555 | executable 1556 | (gethash formatter format-all--install-table)))))))) 1557 | 1558 | (defun format-all--hide-errors-buffer () 1559 | "Internal helper function to update *format-all-errors*." 1560 | (let ((buffer (get-buffer "*format-all-errors*"))) 1561 | (when buffer 1562 | (with-current-buffer buffer 1563 | (let ((inhibit-read-only t)) 1564 | (erase-buffer))) 1565 | (let ((window (get-buffer-window buffer))) 1566 | (when window 1567 | (quit-window nil window)))))) 1568 | 1569 | (defun format-all--update-errors-buffer (status error-output) 1570 | "Internal helper function to update *format-all-errors*. 1571 | 1572 | STATUS and ERROR-OUTPUT come from the formatter." 1573 | (let* ((has-warnings-p (not (= 0 (length error-output)))) 1574 | (has-errors-p (eq status :error)) 1575 | (show-errors-p (cl-case format-all-show-errors 1576 | ((never) nil) 1577 | ((always) t) 1578 | ((warnings) (or has-errors-p has-warnings-p)) 1579 | ((errors) has-errors-p)))) 1580 | (if show-errors-p 1581 | (with-help-window "*format-all-errors*" 1582 | (princ error-output)) 1583 | (format-all--hide-errors-buffer)))) 1584 | 1585 | (defun format-all--save-line-number (thunk) 1586 | "Internal helper function to run THUNK and go back to the same line." 1587 | (let ((old-line-number (line-number-at-pos)) 1588 | ;; NOTE: Not the same as what's returned by `current-column'. 1589 | ;; Column refers to the width of the character, and point 1590 | ;; refers to actual character. For example, if you go over a 1591 | ;; tab character, point will increase by 1, but the column 1592 | ;; with increase by 8 (assuming the width of tab is set to 8). 1593 | (old-line-position (- (point) (line-beginning-position))) 1594 | 1595 | (old-window (selected-window)) 1596 | (old-window-start (window-start))) 1597 | (funcall thunk) 1598 | (goto-char (point-min)) 1599 | (forward-line (1- old-line-number)) 1600 | (let ((line-length (- (line-end-position) (line-beginning-position)))) 1601 | (goto-char (+ (point) (min old-line-position line-length)))) 1602 | (set-window-start old-window old-window-start))) 1603 | 1604 | (defun format-all--save-mark-ring (thunk) 1605 | "Internal helper function to run THUNK and restore the mark ring." 1606 | (let ((old-mark-ring (mapcar #'marker-position mark-ring)) 1607 | (old-mark (marker-position (mark-marker)))) 1608 | (funcall thunk) 1609 | (set-marker (mark-marker) old-mark (current-buffer)) 1610 | (setq mark-ring (mapcar #'copy-marker old-mark-ring)))) 1611 | 1612 | (defun format-all--run-chain (language chain region) 1613 | "Internal function to run a formatter CHAIN on the current buffer. 1614 | 1615 | LANGUAGE is the language ID of the current buffer, from 1616 | `format-all--language-id-buffer`. 1617 | 1618 | REGION is either a (START . END) pair, or nil to format the 1619 | entire buffer." 1620 | (let* ((chain (format-all--normalize-chain chain)) 1621 | (chain-tail chain) 1622 | (error-output "") 1623 | (reformatted-by '())) 1624 | (let ((unsupported 1625 | (when region 1626 | (cl-remove-if 1627 | (lambda (f-name) 1628 | (memq 'region (gethash f-name format-all--features-table))) 1629 | (mapcar #'car chain))))) 1630 | (when unsupported 1631 | (error "The format-all-region command is not supported for %s" 1632 | (string-join (mapcar #'symbol-name unsupported) ", ")))) 1633 | (format-all--save-mark-ring 1634 | (lambda () 1635 | (format-all--save-line-number 1636 | (lambda () 1637 | (cl-loop 1638 | (unless (and chain-tail (= 0 (length error-output))) 1639 | (cl-return)) 1640 | (let* ((formatter (car chain-tail)) 1641 | (f-name (car formatter)) 1642 | (f-args (cdr formatter)) 1643 | (f-function (gethash f-name format-all--format-table)) 1644 | (f-executable (format-all--formatter-executable f-name))) 1645 | (when format-all-debug 1646 | (message 1647 | "Format-All: Formatting %s as %s using %S%s" 1648 | (buffer-name) language f-name 1649 | (with-temp-buffer 1650 | (dolist (arg f-args) (insert " " (shell-quote-argument arg))) 1651 | (buffer-string)))) 1652 | (cl-destructuring-bind (f-output f-error-output) 1653 | (let ((format-all--user-args f-args)) 1654 | (funcall f-function f-executable language region)) 1655 | (let ((f-status :already-formatted)) 1656 | (cond ((null f-output) 1657 | (setq error-output f-error-output) 1658 | (setq f-status :error)) 1659 | ((not (equal f-output t)) 1660 | (setq reformatted-by 1661 | (append reformatted-by (list f-name))) 1662 | (let ((inhibit-read-only t)) 1663 | (erase-buffer) 1664 | (insert f-output)) 1665 | (setq f-status :reformatted))) 1666 | (run-hook-with-args 'format-all-after-format-functions 1667 | f-name f-status) 1668 | (format-all--update-errors-buffer f-status f-error-output)))) 1669 | (setq chain-tail (cdr chain-tail))) 1670 | (message "%s" 1671 | (cond ((not (= 0 (length error-output))) "Formatting error") 1672 | ((not reformatted-by) "Already formatted") 1673 | (t "Reformatted!"))))))))) 1674 | 1675 | (defun format-all--get-default-chain (language) 1676 | "Internal function to get the default formatter chain for LANGUAGE." 1677 | (when language (cdr (assoc language format-all-default-formatters)))) 1678 | 1679 | (defun format-all--get-chain (language) 1680 | "Internal function to get LANGUAGE formatter chain for current buffer." 1681 | (when language (cdr (assoc language format-all-formatters)))) 1682 | 1683 | (defun format-all--set-chain (language chain) 1684 | "Internal function to set LANGUAGE formatter CHAIN for current buffer." 1685 | (cl-assert (stringp language)) 1686 | (cl-assert (listp chain)) 1687 | (setq format-all-formatters 1688 | (append (cl-remove-if (lambda (pair) (equal language (car pair))) 1689 | format-all-formatters) 1690 | (when chain (list (cons language chain)))))) 1691 | 1692 | (defun format-all--prompt-for-formatter (language) 1693 | "Internal function to choose a formatter for LANGUAGE." 1694 | (let ((f-names (gethash language format-all--language-table))) 1695 | (cond ((null f-names) 1696 | (error "No supported formatters for %s" 1697 | (or language "this language"))) 1698 | ((null (cdr f-names)) 1699 | (car f-names)) 1700 | (t 1701 | (let ((f-string (completing-read 1702 | (format "Formatter for %s: " language) 1703 | (mapcar #'list f-names) nil t))) 1704 | (and (not (= 0 (length f-string))) 1705 | (intern f-string))))))) 1706 | 1707 | (defun format-all--buffer-from-hook () 1708 | "Internal helper function to auto-format current buffer from a hook. 1709 | 1710 | Format-All installs this function into `before-save-hook' to 1711 | format buffers on save. This is a lenient version of 1712 | `format-all-buffer' that silently succeeds instead of signaling 1713 | an error if the current buffer has no formatter." 1714 | (let ((language (format-all--language-id-buffer))) 1715 | (format-all--run-chain language 1716 | (format-all--get-chain language) 1717 | nil))) 1718 | 1719 | (defun format-all--buffer-or-region (prompt region) 1720 | "Internal helper function to auto-format current buffer from command. 1721 | 1722 | If PROMPT is non-nil, prompt interactively for formatter. 1723 | If REGION is non-nil, it is a (START . END) pair passed to the formatter." 1724 | (let* ((language (format-all--language-id-buffer)) 1725 | (chain (format-all--get-chain language))) 1726 | (when (or (equal 'always prompt) (and prompt (not chain))) 1727 | (let ((f-name (format-all--prompt-for-formatter language))) 1728 | (when f-name 1729 | (message "Setting formatter to %S" f-name) 1730 | (setq chain (list f-name)) 1731 | (format-all--set-chain language chain)))) 1732 | (unless chain (error "No formatter")) 1733 | (format-all--run-chain language chain region))) 1734 | 1735 | ;;;###autoload 1736 | (defun format-all-region-or-buffer (&optional prompt) 1737 | "Auto-format the source code in the current region or buffer. 1738 | In Transient Mark mode when the mark is active, call `format-all-region'. 1739 | Otherwise, call `format-all-buffer'. 1740 | 1741 | The PROMPT argument works as for `format-all-buffer'." 1742 | (interactive (list (if current-prefix-arg 'always t))) 1743 | (if (use-region-p) 1744 | (format-all-region (region-beginning) (region-end) prompt) 1745 | (format-all-buffer prompt))) 1746 | 1747 | ;;;###autoload 1748 | (defun format-all-buffer (&optional prompt) 1749 | "Auto-format the source code in the current buffer. 1750 | 1751 | No disk files are touched - the buffer doesn't even need to be 1752 | saved. If you don't like the results of the formatting, you can 1753 | use ordinary undo to get your code back to its previous state. 1754 | 1755 | You will need to install external programs to do the formatting. 1756 | If the command can't find the program that it needs, it will try 1757 | to tell you how you might be able to install it on your operating 1758 | system. Only BibTeX, Emacs Lisp and Ledger are formatted without an 1759 | external program. 1760 | 1761 | A suitable formatter is selected according to the `major-mode' of 1762 | the buffer. Many popular programming languages are supported. 1763 | It is fairly easy to add new languages that have an external 1764 | formatter. When called interactively or PROMPT-P is non-nil, a 1765 | missing formatter is prompted in the minibuffer. 1766 | 1767 | If PROMPT is non-nil (or the function is called as an interactive 1768 | command), a missing formatter is prompted in the minibuffer. If 1769 | PROMPT is the symbol `always' (or a prefix argument is given), 1770 | the formatter is prompted for even if one has already been set. 1771 | 1772 | If any errors or warnings were encountered during formatting, 1773 | they are shown in a buffer called *format-all-errors*." 1774 | (interactive (list (if current-prefix-arg 'always t))) 1775 | (format-all--buffer-or-region prompt nil)) 1776 | 1777 | ;;;###autoload 1778 | (defun format-all-region (start end &optional prompt) 1779 | "Auto-format the source code in the current region. 1780 | 1781 | Like `format-all-buffer' but format only the active region 1782 | instead of the entire buffer. This requires support from the 1783 | formatter. 1784 | 1785 | Called non-interactively, START and END delimit the region. 1786 | The PROMPT argument works as for `format-all-buffer'." 1787 | (interactive 1788 | (let ((prompt (if current-prefix-arg 'always t))) 1789 | (if (use-region-p) 1790 | (list (region-beginning) (region-end) prompt) 1791 | (error "The region is not active now")))) 1792 | (format-all--buffer-or-region prompt (cons start end))) 1793 | 1794 | ;;;###autoload 1795 | (defun format-all-ensure-formatter () 1796 | "Ensure current buffer has a formatter, using default if not." 1797 | (interactive) 1798 | (let ((language (format-all--language-id-buffer))) 1799 | (unless (format-all--get-chain language) 1800 | (cond ((not language) 1801 | (message "No formatter for this language")) 1802 | ((not (gethash language format-all--language-table)) 1803 | (message "No formatter for %s" language)) 1804 | (t 1805 | (let ((default (format-all--get-default-chain language))) 1806 | (cond ((not default) 1807 | (message "No default formatter for %s" language)) 1808 | (t 1809 | (message "Using default formatter%s" 1810 | (with-temp-buffer 1811 | (dolist (formatter default (buffer-string)) 1812 | (insert (format " %S" formatter))))) 1813 | (format-all--set-chain language default))))))))) 1814 | 1815 | ;;;###autoload 1816 | (define-minor-mode format-all-mode 1817 | "Toggle automatic source code formatting before save. 1818 | 1819 | When this minor mode (FmtAll) is enabled, `format-all-buffer' is 1820 | automatically called to format your code each time before you 1821 | save the buffer. 1822 | 1823 | The mode is buffer-local and needs to be enabled separately each 1824 | time a file is visited. You may want to use `add-hook' in your 1825 | `user-init-file' to enable the mode based on buffer modes. E.g.: 1826 | 1827 | (add-hook 'prog-mode-hook 'format-all-mode) 1828 | 1829 | To use a default formatter for projects that don't have one, add 1830 | this too: 1831 | 1832 | (add-hook 'prog-mode-hook 'format-all-ensure-formatter) 1833 | 1834 | When `format-all-mode' is called as a Lisp function, the mode is 1835 | toggled if ARG is ‘toggle’, disabled if ARG is a negative integer 1836 | or zero, and enabled otherwise." 1837 | :lighter format-all-mode-lighter 1838 | :global nil 1839 | (if format-all-mode 1840 | (add-hook 'before-save-hook 1841 | 'format-all--buffer-from-hook 1842 | nil 'local) 1843 | (remove-hook 'before-save-hook 1844 | 'format-all--buffer-from-hook 1845 | 'local))) 1846 | 1847 | (provide 'format-all) 1848 | 1849 | ;;; format-all.el ends here 1850 | --------------------------------------------------------------------------------