|
| 1 | +;;; sqlformat.el --- Reformat SQL using sqlformat or pgformatter -*- lexical-binding: t; -*- |
| 2 | + |
| 3 | +;; Copyright (C) 2018 Steve Purcell |
| 4 | + |
| 5 | +;; Author: Steve Purcell <steve@sanityinc.com> |
| 6 | +;; Keywords: languages |
| 7 | +;; Package-Requires: ((emacs "24")) |
| 8 | +;; Version: 0 |
| 9 | + |
| 10 | +;; This program is free software; you can redistribute it and/or modify |
| 11 | +;; it under the terms of the GNU General Public License as published by |
| 12 | +;; the Free Software Foundation, either version 3 of the License, or |
| 13 | +;; (at your option) any later version. |
| 14 | + |
| 15 | +;; This program is distributed in the hope that it will be useful, |
| 16 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | +;; GNU General Public License for more details. |
| 19 | + |
| 20 | +;; You should have received a copy of the GNU General Public License |
| 21 | +;; along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 22 | + |
| 23 | +;;; Commentary: |
| 24 | + |
| 25 | +;; Provides commands and a minor mode for easily reformatting SQL |
| 26 | +;; using external programs such as "sqlformat" and "pg_format". |
| 27 | + |
| 28 | +;; Install the "sqlparse" (Python) package to get "sqlformat", or |
| 29 | +;; "pgformatter" to get "pg_format". |
| 30 | + |
| 31 | +;; Customise the `sqlformat-command' variable as desired, then call |
| 32 | +;; `sqlformat' or `sqlformat-buffer' as convenient. |
| 33 | + |
| 34 | +;; Enable `sqlformat-mode' in SQL buffers like this: |
| 35 | + |
| 36 | +;; (add-hook 'sql-mode-hook 'sqlformat-mode) |
| 37 | + |
| 38 | +;; The `sqlformat' command will then be bound to "C-c C-f" by default. |
| 39 | +;; If `sqlformat-mode-format-on-save' is enabled, this mode will apply |
| 40 | +;; the configured `sqlformat-command' to the buffer every time it is |
| 41 | +;; saved. |
| 42 | + |
| 43 | +;;; Code: |
| 44 | + |
| 45 | + |
| 46 | +;; Minor mode and customisation |
| 47 | + |
| 48 | +(defgroup sqlformat nil |
| 49 | + "Reformat SQL using sqlformat or pgformatter." |
| 50 | + :group 'sql) |
| 51 | + |
| 52 | +(defcustom sqlformat-command 'sqlformat |
| 53 | + "Command used for reformatting. |
| 54 | +This command should receive SQL input via STDIN and output the |
| 55 | +reformatted SQL to STDOUT, returning an appropriate exit code." |
| 56 | + :type '(choice (const :tag "Use \"sqlformat\"" sqlformat) |
| 57 | + (const :tag "Use \"pgformatter\"" pgformatter) |
| 58 | + (string :tag "Custom command"))) |
| 59 | + |
| 60 | +(defcustom sqlformat-mode-format-on-save nil |
| 61 | + "When non-nil, format the buffer on save." |
| 62 | + :type 'boolean) |
| 63 | + |
| 64 | +(defcustom sqlformat-mode-lighter " SQLFmt" |
| 65 | + "Mode lighter for `sqlformat-mode'." |
| 66 | + :type 'string) |
| 67 | + |
| 68 | +(defvar sqlformat-mode-map (make-sparse-keymap) |
| 69 | + "Keymap for `sqlformat-mode'.") |
| 70 | + |
| 71 | +(define-key sqlformat-mode-map (kbd "C-c C-f") 'sqlformat) |
| 72 | + |
| 73 | +(defun sqlformat--on-save () |
| 74 | + "Function called from `before-save-hook' when `sqlformat-mode' is active." |
| 75 | + (when sqlformat-mode-format-on-save |
| 76 | + (sqlformat-buffer))) |
| 77 | + |
| 78 | +;;;###autoload |
| 79 | +(define-minor-mode sqlformat-mode |
| 80 | + "Easy access to SQL reformatting using external programs, optionally including on save." |
| 81 | + :global nil |
| 82 | + :keymap sqlformat-mode-map |
| 83 | + :lighter '(:eval sqlformat-mode-lighter) |
| 84 | + (if sqlformat-mode |
| 85 | + (add-hook 'before-save-hook 'sqlformat--on-save nil t) |
| 86 | + (remove-hook 'before-save-hook 'sqlformat--on-save t))) |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | +;; Commands for reformatting |
| 91 | + |
| 92 | +;;;###autoload |
| 93 | +(defun sqlformat-buffer () |
| 94 | + "Reformat the entire SQL buffer using the `sqlformat' command." |
| 95 | + (interactive) |
| 96 | + (sqlformat (point-min) (point-max))) |
| 97 | + |
| 98 | +;;;###autoload |
| 99 | +(defun sqlformat (beg end) |
| 100 | + "Reformat SQL in region from BEG to END using `sqlformat-command'. |
| 101 | +If no region is active, the current statement (paragraph) is reformatted. |
| 102 | +Install the \"sqlparse\" (Python) package to get \"sqlformat\", or |
| 103 | +\"pgformatter\" to get \"pg_format\"." |
| 104 | + (interactive "r") |
| 105 | + (unless (use-region-p) |
| 106 | + (setq beg (save-excursion |
| 107 | + (backward-paragraph) |
| 108 | + (skip-syntax-forward " >") |
| 109 | + (point)) |
| 110 | + end (save-excursion |
| 111 | + (forward-paragraph) |
| 112 | + (skip-syntax-backward " >") |
| 113 | + (point)))) |
| 114 | + (let ((command (pcase sqlformat-command |
| 115 | + (`sqlformat "sqlformat -r -") |
| 116 | + (`pgformatter "pg_format -") |
| 117 | + (custom custom))) |
| 118 | + (sqlbuf (current-buffer))) |
| 119 | + (with-temp-buffer |
| 120 | + (let ((outbuf (current-buffer))) |
| 121 | + (with-current-buffer sqlbuf |
| 122 | + (message "Command is %S" command) |
| 123 | + (when (zerop (shell-command-on-region beg end command outbuf nil "*sqlformat-errors*" t)) |
| 124 | + (save-excursion |
| 125 | + (delete-region beg end) |
| 126 | + (goto-char beg) |
| 127 | + (insert-buffer-substring outbuf)))))))) |
| 128 | + |
| 129 | + |
| 130 | +(provide 'sqlformat) |
| 131 | +;;; sqlformat.el ends here |
0 commit comments