<commit_before>#+title:  Code Formatting Plugins
#+author: <NAME>
#+email:  <EMAIL>
#+date:   2020:05:25
#+STARTUP: fold

Assorted code formatting plugins.

* Variables

#+begin_src emacs-lisp
  ;; I've commented out several major modes that are either badly supported
  ;; (like sh-mode) or have better support with lsp-mode.
  (defvar my/format-all-major-modes '(;; c-mode
                                      ;; c++-mode
                                      ;; clojure-mode
                                      ;; cmake-mode
                                      cperl-mode
                                      ;; css-mode
                                      ;; elixir-mode
                                      emacs-lisp-mode
                                      erlang-mode
                                      go-mode
                                      haskell-mode
                                      ;; html-mode
                                      ;; java-mode
                                      ;; js-mode
                                      ;; json-mode
                                      ;; LaTeX-mode
                                      ;; less-css-mode
                                      lisp-mode
                                      lisp-data-mode
                                      lua-mode
                                      ;; markdown-mode
                                      ;; nxml-mode
                                      ;; objc-mode
                                      perl-mode
                                      ;; php-mode
                                      ;; python-mode
                                      ;; scss-mode
                                      ;; sgml-mode
                                      ;; Do not manage sh-mode since format-all's shfmt code is bunk.
                                      ;; We have emacs-shfmt to take care of this instead.
                                      ;; sh-mode
                                      ;; toml-mode
                                      ;; yaml-mode
                                      )
    "List of major modes subject to automatic formatting by `format-all'.")
#+end_src

* Functions
#+begin_src emacs-lisp
  (defun my/add-format-all-hooks ()
    "Add hooks for `format-all-mode' to all major modes listed in
  `my/format-all-major-modes'."
    (mapc (lambda (mode)
            (let ((hook-name (concat (symbol-name mode) "-hook")))
              (add-hook (intern hook-name) #'format-all-mode)))
          my/format-all-major-modes))

  (defun my/remove-editorconfig-format-hooks ()
    "Get rid of editorconfig code formatting hooks for major modes managed
  by format-all or some other code formatter."
    (mapc (lambda (mode)
            (let ((hook-name (concat (symbol-name mode) "-hook")))
              (add-hook (intern hook-name) (lambda ()
                                             (remove-hook 'before-save-hook #'editorconfig-format-buffer
                                                          'local)))))
          my/format-all-major-modes))
#+end_src

* format-all
=format-all= is a package for automatically formatting source code files.  It
supports most popular programming and markup languages, although it will often
require third-party plugins to perform its tasks, so be ready to hunt down
some code formatting software.

#+begin_src emacs-lisp
  (use-package format-all
    :disabled
    :demand t
    :after editorconfig
    :init
    (my/remove-editorconfig-format-hooks)
    (my/add-format-all-hooks))
#+end_src

* reformatter
This is a fantastic Emacs framework for defining arbitrary code formatters
contributed by <NAME>. For instance, to define a code formatter for the
=dhall= programming language (assuming we have a predefined =dhall-command=
variable containing the executable command to run), we could write:

#+begin_src emacs-lisp :tangle no
  (reformatter-define dhall-format
    :program dhall-command
    :args '("format")
    :lighter " DF")
#+end_src

The =reformatter-define= macro will expand our code into three functions:
=dhall-format-buffer=, =dhall-format-region=, and =dhall-format-on-save-mode=,
the last of which we can use as a hook for specific major modes.

Bearing all of this in mind, it is now incredibly easy to fill in the gaps
where systems like =format-all= fall short. Thanks, Steve!

#+begin_src emacs-lisp
  (use-package reformatter
    :disabled
    :hook
    (python-mode . my-python-format-on-save-mode)
    :config
    ;; NOTE: It is very important NOT to use our usual 'my/' prefix for these
    ;;       functions because reformatter creates tmp files with the names of our
    ;;       formatter functions, so a slash in the function's name confuses the
    ;;       location of our tmp file.
    (reformatter-define my-cmake-format
      :program "cmake-format"
      :args (list "--inplace"))
    (reformatter-define my-python-format
      :program "black"
      :args (list "-"))
    (reformatter-define my-shell-format
      :program "shfmt"
      :args (list "-")))
#+end_src

* emacs-shfmt
This is an Emacs plugin solely for =shfmt=. =format-all= /does/ have =shfmt=
integration built into it, but it's pretty broken. For instance, due to the
CLI options passed to =shfmt= by =format-all=, spaces will always be replaced
with tabs, and there is no way to change this behavior aside from editing the
code for =format-all= directly, something I refuse to do. This plugin is much
nicer for =shfmt= integration with Emacs.

#+begin_src emacs-lisp
  (use-package shfmt
    :after reformatter
    :hook (sh-mode . shfmt-on-save-mode))
#+end_src

* Apheleia
Apheleia is a wicked fast code auto-formatter plugin for Emacs. Apheleia's main
selling point is that all formatting is done without moving point. Only a few
formatters come pre-configured, such as Black, Prettier, and Gofmt. You must set
all other formatters manually using the =apheleia-formatters= alist and
=apheleia-mode-alist= to automatically activate these formatters for major modes
and filetypes. Check their docstrings for more details.

** Variables
#+begin_src emacs-lisp
  (defvar my/apheleia-custom-formatters
    '((beautysh . ("beautysh"
                   (when-let ((indent (bound-and-true-p sh-basic-offset)))
                     (list "--indent-size" (number-to-string indent)))
                   (when indent-tabs-mode "--tab")
                   "-"))
      (shfmt . ("shfmt" "-s" "-")))
    "Alist associating a name with each custom apheleia formatter.")

  (defvar my/apheleia-mode-formatters-alist
    '((sh-mode . beautysh))
    "Alist mapping major modes to custom formatter specifications for apheleia.
  The car of each alist cons cell can either be a single symbol or
  string, or a list of symbols or strings. If the car is a list,
  each symbol or string in the list will be interpretted as a major
  mode or file name pattern to associate with the cdr, which is a
  formatter name.")
#+end_src

** Functions
#+begin_src emacs-lisp
  (defun my/apheleia-add-formatters (formatter-name-alist)
    "Add apheleia formatters based on FORMATTER-NAME-ALIST.

  FORMATTER-NAME-ALIST associates names to custom apheleia
  formatter specifications. See `apheleia-formatters' for more
  details on how to construct custom formatter specifications."
    (dolist (mode-formatter-pair formatter-name-alist)
      ;; Add custom apheleia formatters to the formatters alist.
      (add-to-list 'apheleia-formatters mode-formatter-pair)))<commit_msg>Refactored emacs formatter plugins config<commit_after>#+title:  Code Formatting Plugins
#+author: <NAME>
#+email:  <EMAIL>
#+date:   2020:05:25
#+STARTUP: fold

Assorted code formatting plugins.

* format-all
=format-all= is a package for automatically formatting source code files.  It
supports most popular programming and markup languages, although it will often
require third-party plugins to perform its tasks, so be ready to hunt down
some code formatting software.

** Variables

#+begin_src emacs-lisp
  ;; I've commented out several major modes that are either badly supported
  ;; (like sh-mode) or have better support with lsp-mode.
  (defvar my/format-all-major-modes '(;; c-mode
                                      ;; c++-mode
                                      ;; clojure-mode
                                      ;; cmake-mode
                                      cperl-mode
                                      ;; css-mode
                                      ;; elixir-mode
                                      emacs-lisp-mode
                                      erlang-mode
                                      go-mode
                                      haskell-mode
                                      ;; html-mode
                                      ;; java-mode
                                      ;; js-mode
                                      ;; json-mode
                                      ;; LaTeX-mode
                                      ;; less-css-mode
                                      lisp-mode
                                      lisp-data-mode
                                      lua-mode
                                      ;; markdown-mode
                                      ;; nxml-mode
                                      ;; objc-mode
                                      perl-mode
                                      ;; php-mode
                                      ;; python-mode
                                      ;; scss-mode
                                      ;; sgml-mode
                                      ;; Do not manage sh-mode since format-all's shfmt code is bunk.
                                      ;; We have emacs-shfmt to take care of this instead.
                                      ;; sh-mode
                                      ;; toml-mode
                                      ;; yaml-mode
                                      )
    "List of major modes subject to automatic formatting by `format-all'.")
#+end_src

** Functions
#+begin_src emacs-lisp
  (defun my/add-format-all-hooks ()
    "Add hooks for `format-all-mode' to all major modes listed in
  `my/format-all-major-modes'."
    (mapc (lambda (mode)
            (let ((hook-name (concat (symbol-name mode) "-hook")))
              (add-hook (intern hook-name) #'format-all-mode)))
          my/format-all-major-modes))

  (defun my/remove-editorconfig-format-hooks ()
    "Get rid of editorconfig code formatting hooks for major modes managed
  by format-all or some other code formatter."
    (mapc (lambda (mode)
            (let ((hook-name (concat (symbol-name mode) "-hook")))
              (add-hook (intern hook-name) (lambda ()
                                             (remove-hook 'before-save-hook #'editorconfig-format-buffer
                                                          'local)))))
          my/format-all-major-modes))
#+end_src

** use-package specification
#+begin_src emacs-lisp
  (use-package format-all
    ;; Disabled in favor of apheleia.
    :disabled
    :demand t
    :after editorconfig
    :init
    (my/remove-editorconfig-format-hooks)
    (my/add-format-all-hooks))
#+end_src

* reformatter
This is a fantastic Emacs framework for defining arbitrary code formatters
contributed by <NAME>. For instance, to define a code formatter for the
=dhall= programming language (assuming we have a predefined =dhall-command=
variable containing the executable command to run), we could write:

#+begin_src emacs-lisp :tangle no
  (reformatter-define dhall-format
    :program dhall-command
    :args '("format")
    :lighter " DF")
#+end_src

The =reformatter-define= macro will expand our code into three functions:
=dhall-format-buffer=, =dhall-format-region=, and =dhall-format-on-save-mode=,
the last of which we can use as a hook for specific major modes.

Bearing all of this in mind, it is now incredibly easy to fill in the gaps
where systems like =format-all= fall short. Thanks, Steve!

#+begin_src emacs-lisp
  (use-package reformatter
    ;; Disabled in favor of apheleia.
    :disabled
    :hook
    (python-mode . my-python-format-on-save-mode)
    :config
    ;; NOTE: It is very important NOT to use our usual 'my/' prefix for these
    ;;       functions because reformatter creates tmp files with the names of our
    ;;       formatter functions, so a slash in the function's name confuses the
    ;;       location of our tmp file.
    (reformatter-define my-cmake-format
      :program "cmake-format"
      :args (list "--inplace"))
    (reformatter-define my-python-format
      :program "black"
      :args (list "-"))
    (reformatter-define my-shell-format
      :program "shfmt"
      :args (list "-")))
#+end_src

* Apheleia
Apheleia is a wicked fast code auto-formatter plugin for Emacs. Apheleia's main
selling point is that all formatting is done without moving point. Only a few
formatters come pre-configured, such as Black, Prettier, and Gofmt. You must set
all other formatters manually using the =apheleia-formatters= alist and
=apheleia-mode-alist= to automatically activate these formatters for major modes
and filetypes. Check their docstrings for more details.

** Variables
#+begin_src emacs-lisp
  (defvar my/apheleia-custom-formatters
    '((beautysh . ("beautysh"
                   (when-let ((indent (bound-and-true-p sh-basic-offset)))
                     (list "--indent-size" (number-to-string indent)))
                   (when indent-tabs-mode "--tab")
                   "-"))
      (shfmt . ("shfmt" "-s" "-")))
    "Alist that maps names to custom apheleia formatters.
  It is perfectly acceptable to define formatters for executables
  that may or may not be installed on the filesystem. They will not
  be called unless the formatter name is listed in
  `apheleia-mode-alist'.")

  (defvar my/apheleia-mode-formatters-alist
    '((sh-mode . beautysh))
    "Alist mapping major modes to custom formatter specifications for apheleia.
  The car of each alist cons cell can either be a single symbol or
  string, or a list of symbols or strings. If the car is a list,
  each symbol or string in the list will be interpretted as a major
  mode or file name pattern to associate with the cdr, which is a
  formatter name.")
#+end_src

** Functions
#+begin_src emacs-lisp
  (defun my/apheleia-add-formatters (formatter-name-alist)
    "Add apheleia formatters based on FORMATTER-NAME-ALIST.

  FORMATTER-NAME-ALIST associates names to custom apheleia
  formatter specifications. See `apheleia-formatters' for more
  details on how to construct custom formatter specifications."
    (dolist (mode-formatter-pair formatter-name-alist)
      ;; Add custom apheleia formatters to the formatters alist.
      (add-to-list 'apheleia-formatters mode-formatter-pair)))
{'fraction_non_alphanumeric': 0.08539371136791185, 'fraction_numerical': 0.0010749798441279225, 'mean_word_length': 1.7127756515400037, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 120, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1997886', 'n_tokens_mistral': 4158, 'n_tokens_neox': 3866, 'n_words': 1354}