From 60a4393fa3748d94e3aaa0d6253cfe11ad0089eb Mon Sep 17 00:00:00 2001 From: Zhang Yueqian Date: Sun, 9 Oct 2022 15:40:25 +0800 Subject: [PATCH] misc: update --- elisp/cygwin-mount.el | 565 +++ elisp/format-all.el | 1592 +++++++ elisp/icons/close.xpm | 40 + elisp/icons/leaf.xpm | 34 + elisp/icons/open.xpm | 39 + elisp/inheritenv.el | 89 + elisp/language-id.el | 276 ++ elisp/loaddefs.el | 113 + elisp/markdown-mode.el | 9968 ++++++++++++++++++++++++++++++++++++++++ elisp/my-minor-mode.el | 25 + elisp/neotree.el | 2225 +++++++++ elisp/setup-cygwin.el | 278 ++ elisp/yaml-mode.el | 491 ++ emacs.vbs | 25 + init.el | 190 + 15 files changed, 15950 insertions(+) create mode 100755 elisp/cygwin-mount.el create mode 100755 elisp/format-all.el create mode 100755 elisp/icons/close.xpm create mode 100755 elisp/icons/leaf.xpm create mode 100755 elisp/icons/open.xpm create mode 100755 elisp/inheritenv.el create mode 100755 elisp/language-id.el create mode 100755 elisp/loaddefs.el create mode 100755 elisp/markdown-mode.el create mode 100755 elisp/my-minor-mode.el create mode 100755 elisp/neotree.el create mode 100755 elisp/setup-cygwin.el create mode 100755 elisp/yaml-mode.el create mode 100755 emacs.vbs create mode 100755 init.el diff --git a/elisp/cygwin-mount.el b/elisp/cygwin-mount.el new file mode 100755 index 0000000..e43de70 --- /dev/null +++ b/elisp/cygwin-mount.el @@ -0,0 +1,565 @@ +;;; cygwin-mount.el --- Teach EMACS about cygwin styles and mount points. + +;; Copyright (C) 1997 Michael Cook . +;; 2001 Klaus Berndl + +;; Author: Michael Cook +;; Keywords: files, mount, cygwin + +;; This file is *NOT* (yet?) part of GNU Emacs. +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;; Additional info: +;; $Date: 2004-01-14 07:35:04 -0800 (Wed, 14 Jan 2004) $ +;; Maintenance: Eric Hanchrow +;; Additional code by: Stephane Rollandin +;; Michael Mauger +;; Keisuke Mori +;; Drew Moseley (drewmoseley@mindspring.com) +;; James Ganong (jeg@bigseal.ucsc.edu) +;; Jeff Juliano +;; Klaus Berndl +;; Nick Sieger +;; Richard Y. Kim +;; Karel Sprenger +;; +;; When submitting patches, please try to also submit an automated +;; test that demonstrates the need for the patch -- i.e., one that +;; fails with the unpatched code, but passes with the patched code. +;; This will make is easier for the maintainer to decide if your patch +;; duplicates functionality from other patches. Patching `tests.el' +;; would be good. +;;; Availabilty + +;; The latest version of cygwin-mount.el can always be found at +;; http://www.blarg.net/~offby1/cygwin-mount/ + +;; ---------------------------------------------------------------------- +;;; Commentary + +;; This package lets you use cygwin-style filenames like +;; "//D/any/path/to/file" or "/cygdrive/D/any/path/to/file" in exactly +;; the same manner as the normal Windows-style filenames like +;; "D:\any\path\to\file" or "D:/any/path/to/file". NOTE: "/cygdrive/" +;; is only an example for the cygdrive-prefix \(see +;; `cygwin-mount-cygdrive-prefix--internal'). cygwin-mount can handle +;; every cygdrive-prefix set by "mount --change-cygdrive-prefix" +;; (e.g. "/" is also a valid cygdrive-prefix). UNC paths work +;; too. Furthermore, this package lets you use all your cygwin mounts +;; in file operations. For example, you can use (e.g. find-file) for +;; a file named "/usr/bin/anyfile" if you have mounted the related +;; Windows-path to /usr/bin. Ange-ftp also works correctly. + +;;; Installation: + +;; Put in your .emacs or site-start.el file the following lines: +;; (require 'cygwin-mount) +;; (cygwin-mount-activate) + +;;; Customization and using + +;; + All customization is done in the customize-group `cygwin-mount'. +;; Do not set the user-options via `setq' but only via customize because +;; otherwise the package will not work correct!! +;; Important: Now the cygwin mountpoints are stored in the variable +;; `cygwin-mount-table--internal'. This variable must not be set by the user +;; but it is only set by customizing `cygwin-mount-table'! +;; + Activating: cygwin-mount-activate +;; + Deactivating: cygwin-mount-deactivate + +;;; Compatibility + +;; The cygwin-mount.el package is only tested with NTEmacs >= 20.6.1. +;; It has also been tested with the native port of XEmacs 21.1 but not +;; very intensive! The package has been tested with Cygwin 1.1.8 and +;; >= 1.3.1. It reportedly also works with at least some versions of +;; MinGW. + +;; How it works: +;; basically we push some functions onto file-name-handler-alist. +;; They detect filenames expressed in cygwin style, and translate +;; those names into native Win32 style. + +;;; Code: + +(defconst cygwin-mount-version "1.4.8") + +(defgroup cygwin-mount nil + "Proper handling of cygwin mounts and filenames." + :prefix "cygwin-" + :group 'files) + +;; some constants + +(defconst cygwin-mount-program "mount.exe") +(defconst cygwin-mount-uname-program "uname.exe") +(defconst cygwin-mount-buffername " *mount*") + +;; internal variables. These variables are only set by calling +;; `cygwin-mount-activate' or by customizing `cygwin-mount-table'. + +(defvar cygwin-mount-table--internal nil + "Do not set this variable directly but customize `cygwin-mount-table'!") + +(defvar cygwin-mount-cygdrive-prefix--internal "" + "Prefix for the \"/cygdrive/X/\" style of cygwin. +A cygwin-user can change the \"/cygdrive\" to whatever he wants to access +files at MS-DOS drives. For example many people seem to like to have the +drives accessible as a directory so that c: == /c, which means the +cygdrive-prefix is \"/\" instead of \"/cygdrive\". This prefix must end +with a '/'! Do not set this variable because the value of this variable is +determined at activation-time of cygwin-mount \(see +`cygwin-mount-activate')") + +;; user options + +(defcustom cygwin-mount-cygwin-bin-directory nil + "*The directory where the cygwin binaries reside. +If nil then the cygwin-binary-directory must be into the PATH." + :group 'cygwin-mount + :type '(radio (const :tag "Cygwin is into PATH" :value nil) + (directory :tag "Cygwin-Binary-Dir" :value ""))) + +(defcustom cygwin-mount-build-mount-table-asynch nil + "*When non-nil, `cygwin-mount-table' is built at load-time. +If you change the value via customize you must deactivate and activate the +package again to take effect." + :group 'cygwin-mount + :type 'boolean) + +(defcustom cygwin-mount-table t + "*Alist of custom cygwin mount points or t. +If t if all the current mount-points returned by the cygwin mount-program +should be used. If set to nil then there are no mount-points. An element of +the alist has the form \( . ), +e.g. \(\"D:\\\\programs\\\\cygwin\\\\bin\" . \"/usr/bin/\") or +\(\"D:/programs/cygwin\" . \"/\")." + :group 'cygwin-mount + :set (function (lambda (symbol value) + (set symbol value) + (if (equal value t) + (cygwin-mount-build-table-internal) + (setq cygwin-mount-table--internal value)))) + :initialize 'custom-initialize-default + :type '(radio (const :tag "Automatic" + :value t) + (repeat :tag "Custom mounts" + (cons (directory :tag "Mounted device") + (string :tag "Cygwin directory"))))) + +;; copied from executable.el because this library is not included in all +;; Emacsen by default. +(defvar cygwin-mount-executable-binary-suffixes + (if (memq system-type '(ms-dos windows-nt)) + '(".exe" ".com" ".bat" ".cmd" ".btm" "") + '(""))) +(defun cygwin-mount-executable-find (command) + "Search for COMMAND in `exec-path' and return the absolute file name. +Return nil if COMMAND is not found anywhere in `exec-path'." + (let ((list exec-path) + file) + (while list + (setq list + (if (and (setq file (expand-file-name command (car list))) + (let ((suffixes cygwin-mount-executable-binary-suffixes) + candidate) + (while suffixes + (setq candidate (concat file (car suffixes))) + (if (and (file-executable-p candidate) + (not (file-directory-p candidate))) + (setq suffixes nil) + (setq suffixes (cdr suffixes)) + (setq candidate nil))) + (setq file candidate))) + nil + (setq file nil) + (cdr list)))) + file)) + +;; functions + +(defun cygwin-mount-get-full-progname (program) + "Return the full path of PROGRAM if possible or nil." + (let ((fullname + (if (and cygwin-mount-cygwin-bin-directory + (stringp cygwin-mount-cygwin-bin-directory) + (file-directory-p cygwin-mount-cygwin-bin-directory)) + (concat (file-name-as-directory cygwin-mount-cygwin-bin-directory) + program) + (expand-file-name (or (cygwin-mount-executable-find program) + program))))) + (if (file-executable-p fullname) + fullname + nil))) + +(defun cygwin-mount-get-cygdrive-prefix () + "Return prefix used for the \"/cygdrive/X/\" style of cygwin. +This is done by calling \"mount --show-cygdrive-prefixes\". +The result is either \"/\" or \"//\"." + (let ((buf (get-buffer-create " *cygdrive*")) + (fullname (cygwin-mount-get-full-progname cygwin-mount-program))) + (if (null fullname) + (error "Cannot find program '%s'. Check `cygwin-mount-cygwin-bin-directory'" + cygwin-mount-program) + (with-current-buffer buf + (or + (progn + (erase-buffer) + (zerop (call-process fullname nil buf nil "--show-cygdrive-prefix"))) + (progn + (erase-buffer) + (zerop (call-process fullname nil buf nil "--show-cygdrive-prefixes"))) + (error "Cannot run %s" fullname)) + (goto-char (point-min)) + (prog1 + (let ((regexp-prefix "\\(/[^ \t]*\\)[ \t]+") + cygdrive-prefix) + ;; we search first for the user prefix and if there isn't any we + ;; search for the system prefix. + (if (or (search-forward-regexp (concat regexp-prefix "user") nil t) + (search-forward-regexp (concat regexp-prefix "system") nil t)) + (progn + (setq cygdrive-prefix (match-string 1)) + (if (string= cygdrive-prefix "/") + cygdrive-prefix + (concat cygdrive-prefix "/"))) + "/cygdrive/")) + (kill-buffer buf)))))) + +(defun trim-trailing-whitespace (str) + (string-match "^\\(.*\\S-\\)\\s-*$" str ) + (replace-match "\\1" nil nil str)) + +(defun cygwin-mount-parse-one-line (line) + "Parse the output from one line of the Cygwin `mount' command; + return a pair containing the windows directory and the corresponding + Cygwin path." + + ;; can't use non-greedy regular expressions because versions of + ;; Emacs older than 21.1 lack them. + (if (or (string-match "\\(.*\\) on \\(/.*\\) type .* (.*)" line) + (string-match "\\(.*\\)\\s-+\\(/.*\\)\\s-+\\(user\\|system\ +\\|vfat\\)\\s-+\\(textmode\\|binmode\\)" + line)) + (let ((win (match-string 1 line)) + (cyg (match-string 2 line))) + (setq win (trim-trailing-whitespace win)) + (setq cyg (trim-trailing-whitespace cyg)) + (cons win cyg)) + (error "Cannot parse output from `mount': %s" line))) + +(defun cygwin-mount-parse-mount () + "Parse buffer `cygwin-mount-buffername' and return alist of +mount-points, sorted with longest \"device\" names first. For the +format of this alist see `cygwin-mount-table'. Precondition of this +function is current buffer must be the buffer named +`cygwin-mount-buffername'." + (if (equal (current-buffer) (get-buffer cygwin-mount-buffername)) + (let ((case-fold-search t) + mounts) + (goto-char (point-min)) + (while (not (eobp)) + (let* ((parsed (cygwin-mount-parse-one-line + (buffer-substring + (progn (beginning-of-line) (point)) + (progn (end-of-line) (point))))) + (device (car parsed)) + (direct (cdr parsed))) + (setq mounts (cons (cons (file-name-as-directory device) + (file-name-as-directory direct)) + mounts)) + (forward-line 1))) + + ;; now sort the alist so that the longest directories come first. + (setq mounts (sort mounts (function (lambda (pair1 pair2) + (> (length (cdr pair1)) + (length (cdr pair2)))))) ) + mounts))) + +(defun cygwin-mount-sentinel (proc msg) + "Process sentinel for PROC with MSG." + (if (or (eq (process-status proc) 'exit) + (eq (process-status proc) 'signal)) + (let ((buf (get-buffer-create cygwin-mount-buffername))) + (with-current-buffer buf + (setq cygwin-mount-table--internal (cygwin-mount-parse-mount))) + (kill-buffer buf) + (message "Build of mount table completed")))) + +(defun cygwin-mount-build-table-internal () + "Determine cygwin mount points. +This function sets `cygwin-mount-table--internal' +either synchronously or asynchronously \(see +`cygwin-mount-build-mount-table-asynch'). If asynchronously then the set is +really done by `cygwin-mount-sentinel'." + (let ((fullname (cygwin-mount-get-full-progname cygwin-mount-program))) + (if (null fullname) + (error "Cannot find program '%s'. Check `cygwin-mount-cygwin-bin-directory'" + cygwin-mount-program) + (if cygwin-mount-build-mount-table-asynch + ;; asynchron building + (let ((proc (start-process "mount" cygwin-mount-buffername fullname))) + (set-process-sentinel proc 'cygwin-mount-sentinel) + ;; 2013-10-21, TunaFish5 (EmacsWiki): Use `set-process-query-on-exit-flag'. + ;; D. Adams: Use only if defined. Keep older code for older Emacs. + (if (fboundp 'set-process-query-on-exit-flag) + (set-process-query-on-exit-flag proc nil) + (process-kill-without-query proc))) + ;; synchron building + (let ((buf (get-buffer-create cygwin-mount-buffername))) + (with-current-buffer buf + (erase-buffer) + (call-process fullname nil buf) + (prog1 + (setq cygwin-mount-table--internal (cygwin-mount-parse-mount)) + (kill-buffer buf)))))))) + +(defun cygwin-mount-name-hook-function (operation &rest args) + "Run OPERATION with ARGS." + (let ((fn (get operation 'cygwin-mount-name))) + (if fn (apply fn operation args) + (cygwin-mount-run-real-handler operation args)))) + +(defun cygwin-mount-map-drive-hook-function (operation &rest args) + "Run OPERATION with ARGS." + (let ((fn (get operation 'cygwin-mount-map-drive))) + (if fn (apply fn operation args) + (cygwin-mount-run-real-handler operation args)))) + +(defun cygwin-mount-run-real-handler (operation args) + "Run OPERATION with ARGS." + (let ((inhibit-file-name-handlers + (append '(cygwin-mount-name-hook-function + cygwin-mount-map-drive-hook-function) + (and (eq inhibit-file-name-operation operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) + (apply operation args))) + + +(defun cygwin-mount-name-expand (operation name &rest args) + "Run OPERATION NAME with ARGS. + first ARG is either nil or a file name" + (when (and args (car args)) + (setq args (copy-sequence args)) ; TODO: determine if this call is necessary + (setcar args (cygwin-mount-substitute-longest-mount-name (car args)))) + (cygwin-mount-run-real-handler + operation + (cons (cygwin-mount-substitute-longest-mount-name name) args))) + +(defun cygwin-mount-substitute-longest-mount-name (name) + "Substitute NAME with mount device or return NAME." + (and name + (save-match-data + (if (or (string-match "^//.+" name) (string-match "/\\[.+\\]" name)) + ;; Added by Klaus: if name beginns with "//" then it can never contain + ;; a cygwin mount point! Therefore we must not check for contained + ;; mount points because if / is mounted then this will always match + ;; and we get an incorrect substitution for network devices like + ;; //Host/path + name + (let ((mounts cygwin-mount-table--internal) + (len (length (file-name-as-directory name))) + match) + (while mounts + (let ((mount (file-name-as-directory (cdar mounts)))) + (and (>= len (length mount)) + (string= mount + (file-name-as-directory + (substring (file-name-as-directory name) + 0 (length mount)))) + (or (null match) + (> (length (cdar mounts)) (length (cdr match)))) + (setq match (car mounts)))) + (setq mounts (cdr mounts))) + (if match + (concat (file-name-as-directory (car match)) + (if (>= (length (file-name-as-directory (cdr match))) len) + "" + (substring name (length (file-name-as-directory (cdr match)))))) + name)))))) + +;; Added by Klaus +(defconst cygwin-mount-cygwin-style1-regexp "^/[^:@]*$\\|^/|/[^/:]+\\(\\'\\|/\\)") + +;; This appears to work with ancient versions of cygwin, on which the +;; cygwin path `//x' was shorthand for the Win32 path `x:'. +(defconst cygwin-mount-cygwin-style2-regexp "^//[A-Za-z]/") + +;; will be set by `cygwin-mount-activate'. +(defvar cygwin-mount-cygwin-style3-regexp nil) + +;; We cannot assume that NAME matched cygwin-mount-cygwin-style1-regexp, +;; cygwin-mount-cygwin-style2-regexp nor cygwin-mount-cygwin-style3-regexp, +;; because this function could be called with either argument to +;; `expand-file-name', but only one argument to `expand-file-name' may +;; have matched a regexp. +;; For example, `(expand-file-name ".." "/cygdrive/c/")' will trigger +;; `(cygwin-mount-convert-file-name "..")' and +;; `(cygwin-mount-convert-file-name "/cygdrive/c/")' to be called. +(defun cygwin-mount-convert-file-name ( name ) + "Convert file NAME, to cygwin format. +`//x/' to `x:/' and `/cygdrive/x/' to `x:/'. +NOTE: \"/cygdrive/\" is only an example for the cygdrive-prefix \(see +`cygwin-mount-cygdrive-prefix--internal')." + (let ((cygdrive-prefix-len (length cygwin-mount-cygdrive-prefix--internal))) + (save-match-data + (cond ((string-match cygwin-mount-cygwin-style2-regexp name) + (concat (substring name 2 3) ":" (substring name 3))) + ((string-match cygwin-mount-cygwin-style3-regexp name) + (concat (substring name cygdrive-prefix-len + (1+ cygdrive-prefix-len)) + ":" (substring name (1+ cygdrive-prefix-len) nil))) + (t name))))) + +(defun cygwin-mount-map-drive (operation name &rest args) + "Run OPERATION on cygwin NAME with ARGS. +Map cygwin name to the dos-style \"[A-Za-z]:/\" and call +OPERATION with the mapped filename\(s). NAME must have the format looks like +\"^//[A-Za-z]/\" or \"/cygdrive/[A-Za-z]/\" here. Note that at least the first +element of ARGS could be a filename too \(then it must have the same syntax +like NAME!) which must be converted \(e.g. `expand-file-name' can be called +with two filenames). +NOTE: \"/cygdrive/\" is only an example for the cygdrive-prefix \(see +`cygwin-mount-cygdrive-prefix--internal')." + (cygwin-mount-run-real-handler + operation + (cons (cygwin-mount-convert-file-name name) + (if (stringp (car args)) + (cons (cygwin-mount-convert-file-name (car args)) + (cdr args)) + args)))) + +;;; TODO -- see if we need to do stuff for Tramp that is similar to +;;; what we're about to do for ange-ftp. If so, perhaps we can use +;;; `advice' to clean up those forms below whose comments describe +;;; them as "real hacks". + +;;; ange-ftp +(if (locate-library "ange-ftp") + (require 'ange-ftp)) + +;;; save the original function definition of ange-ftp-run-real-handler +(defconst cygwin-mount-original-ange-ftp-handler + (if (featurep 'ange-ftp) + (symbol-function 'ange-ftp-run-real-handler) + nil)) + +;;; This version of ange-ftp-run-real-handler also inhibits the +;;; cygwin file name expansion when we are doing ange-ftp expansion. +;;; +;;; This is a real hack. If the real definition of this function +;;; changes, we have to modify this function +(defun cygwin-mount-ange-ftp-run-real-handler (operation args) + "Run OPERATION with ARGS." + (let ((inhibit-file-name-handlers + (append '(ange-ftp-hook-function + ange-ftp-completion-hook-function + cygwin-mount-name-hook-function + cygwin-mount-map-drive-hook-function) + (and (eq inhibit-file-name-operation + operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) + (apply operation args))) + +;; Added by Klaus +(defvar cygwin-mount-activated nil) +(defun cygwin-mount-activate () + "Activate cygwin-mount- and cygwin-style-handling." + (interactive) + (if (not (eq system-type 'windows-nt)) + (message "cygwin-mount is only available for Emacs for NT!") + + (unless cygwin-mount-activated + ;; initialize the internal variables + + (if (equal cygwin-mount-table t) + (cygwin-mount-build-table-internal) + (setq cygwin-mount-table--internal cygwin-mount-table)) + (setq cygwin-mount-cygdrive-prefix--internal + (cygwin-mount-get-cygdrive-prefix)) + (setq cygwin-mount-cygwin-style3-regexp + (concat "^" cygwin-mount-cygdrive-prefix--internal "[A-Za-z]/")) + + ;; add the cygwin-filehandlers + (or (assoc cygwin-mount-cygwin-style1-regexp file-name-handler-alist) + (setq file-name-handler-alist + (cons `(,cygwin-mount-cygwin-style1-regexp + . cygwin-mount-name-hook-function) + file-name-handler-alist))) + + (or (assoc cygwin-mount-cygwin-style2-regexp file-name-handler-alist) + (setq file-name-handler-alist + (cons `(,cygwin-mount-cygwin-style2-regexp + . cygwin-mount-map-drive-hook-function) + file-name-handler-alist))) + (or (assoc cygwin-mount-cygwin-style3-regexp file-name-handler-alist) + (setq file-name-handler-alist + (cons `(,cygwin-mount-cygwin-style3-regexp + . cygwin-mount-map-drive-hook-function) + file-name-handler-alist))) + + ;; add cygwin-properties + (put 'substitute-in-file-name 'cygwin-mount-name + 'cygwin-mount-name-expand) + (put 'expand-file-name 'cygwin-mount-name 'cygwin-mount-name-expand) + (put 'substitute-in-file-name + 'cygwin-mount-map-drive 'cygwin-mount-map-drive) + (put 'expand-file-name 'cygwin-mount-map-drive + 'cygwin-mount-map-drive) + ;; rebind ange-ftp-run-real-handler to our version + (if (featurep 'ange-ftp) + (fset 'ange-ftp-run-real-handler 'cygwin-mount-ange-ftp-run-real-handler)) + + (setq cygwin-mount-activated t)))) + +;; Added by Klaus +(defun cygwin-mount-deactivate () + "Deactivate cygwin-mount- and cygwin-style-handling." + (interactive) + (if (not (eq system-type 'windows-nt)) + (message "cygwin-mount is only available for (X)Emacs for NT!") + (unless (not cygwin-mount-activated) + ;; reset the internal variables + (setq cygwin-mount-table--internal nil) + + (setq cygwin-mount-cygdrive-prefix--internal "") + + ;; remove the cygwin-filehandlers + (setq file-name-handler-alist + (delete (assoc cygwin-mount-cygwin-style1-regexp file-name-handler-alist) + file-name-handler-alist)) + (setq file-name-handler-alist + (delete (assoc cygwin-mount-cygwin-style2-regexp file-name-handler-alist) + file-name-handler-alist)) + (setq file-name-handler-alist + (delete (assoc cygwin-mount-cygwin-style3-regexp file-name-handler-alist) + file-name-handler-alist)) + ;; remove the cygwin properties + (put 'substitute-in-file-name 'cygwin-mount-name nil) + (put 'expand-file-name 'cygwin-mount-name nil) + (put 'substitute-in-file-name 'cygwin-mount-map-drive nil) + (put 'expand-file-name 'cygwin-mount-map-drive nil) + ;; rebind ange-ftp-run-real-handler to its original definition. + (if (featurep 'ange-ftp) + (fset 'ange-ftp-run-real-handler cygwin-mount-original-ange-ftp-handler)) + (setq cygwin-mount-activated nil)))) + +(provide 'cygwin-mount) + +;;; cygwin-mount.el ends here diff --git a/elisp/format-all.el b/elisp/format-all.el new file mode 100755 index 0000000..287a71e --- /dev/null +++ b/elisp/format-all.el @@ -0,0 +1,1592 @@ +;;; format-all.el --- Auto-format C, C++, JS, Python, Ruby and 50 other languages -*- lexical-binding: t -*- + +;; Author: Lassi Kortela +;; URL: https://github.com/lassik/emacs-format-all-the-code +;; Version: 0.5.0 +;; Package-Requires: ((emacs "24.4") (inheritenv "0.1") (language-id "0.19")) +;; Keywords: languages util +;; SPDX-License-Identifier: MIT + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Lets you auto-format source code in many languages using the same +;; command for all languages, instead of learning a different Emacs +;; package and formatting command for each language. + +;; Just do M-x format-all-buffer and it will try its best to do the +;; right thing. To auto-format code on save, use the minor mode +;; format-all-mode. Please see the documentation for that function +;; for instructions. + +;; Supported languages: + +;; - Angular/Vue (prettier) +;; - Assembly (asmfmt) +;; - ATS (atsfmt) +;; - Awk (gawk) +;; - Bazel Starlark (buildifier) +;; - BibTeX (Emacs) +;; - C/C++/Objective-C (clang-format, astyle) +;; - C# (clang-format, astyle) +;; - Cabal (cabal-fmt) +;; - Clojure/ClojureScript (zprint, node-cljfmt) +;; - CMake (cmake-format) +;; - Crystal (crystal tool format) +;; - CSS/Less/SCSS (prettier) +;; - Cuda (clang-format) +;; - D (dfmt) +;; - Dart (dartfmt, dart-format) +;; - Dhall (dhall format) +;; - Dockerfile (dockfmt) +;; - Elixir (mix format) +;; - Elm (elm-format) +;; - Emacs Lisp (Emacs) +;; - Erlang (efmt) +;; - F# (fantomas) +;; - Fish Shell (fish_indent) +;; - Fortran Free Form (fprettify) +;; - Gleam (gleam format) +;; - GLSL (clang-format) +;; - Go (gofmt, goimports) +;; - GraphQL (prettier) +;; - Haskell (brittany, fourmolu, hindent, ormolu, stylish-haskell) +;; - HTML/XHTML/XML (tidy) +;; - Java (clang-format, astyle) +;; - JavaScript/JSON/JSX (prettier, standard) +;; - Jsonnet (jsonnetfmt) +;; - Kotlin (ktlint) +;; - LaTeX (latexindent, auctex) +;; - Ledger (ledger-mode) +;; - Lua (lua-fmt, stylua, prettier plugin) +;; - Markdown (prettier) +;; - Nginx (nginxfmt) +;; - Nix (nixpkgs-fmt, nixfmt) +;; - OCaml (ocp-indent) +;; - Perl (perltidy) +;; - PHP (prettier plugin) +;; - Protocol Buffers (clang-format) +;; - PureScript (purty, purs-tidy) +;; - Python (black, yapf, isort) +;; - R (styler) +;; - Racket (raco-fmt) +;; - Reason (bsrefmt) +;; - ReScript (rescript) +;; - Ruby (rubocop, rufo, standardrb) +;; - Rust (rustfmt) +;; - Scala (scalafmt) +;; - Shell script (beautysh, shfmt) +;; - Snakemake (snakefmt) +;; - Solidity (prettier plugin) +;; - SQL (pgformatter, sqlformat) +;; - Svelte (prettier plugin) +;; - Swift (swiftformat) +;; - Terraform (terraform fmt) +;; - TOML (prettier plugin) +;; - TypeScript/TSX (prettier, ts-standard) +;; - V (v fmt) +;; - Verilog (iStyle) +;; - YAML (prettier) +;; - Zig (zig) + +;; You will need to install external programs to do the formatting. +;; If `format-all-buffer` can't find the right program, it will try to +;; tell you how to install it. + +;; Many of the external formatters support configuration files in the +;; source code directory to control their formatting. Please see the +;; documentation for each formatter. + +;; New external formatters can be added easily if they can read code +;; from standard input and format it to standard output. Feel free to +;; submit a pull request or ask for help in GitHub issues. + +;;; Code: + +(require 'subr-x) +(require 'cl-lib) +(require 'inheritenv) +(require 'language-id) + +(defgroup format-all nil + "Lets you auto-format source code." + :group 'format-all) + +(defcustom format-all-debug nil + "When non-nil, troubleshooting info is written into the *Messages* buffer." + :type 'boolean + :group 'format-all) + +(defcustom format-all-default-formatters + '(("Assembly" asmfmt) + ("ATS" atsfmt) + ("Bazel" buildifier) + ("BibTeX" emacs-bibtex) + ("C" clang-format) + ("C#" clang-format) + ("C++" clang-format) + ("Cabal Config" cabal-fmt) + ("Clojure" zprint) + ("CMake" cmake-format) + ("Crystal" crystal) + ("CSS" prettier) + ("Cuda" clang-format) + ("D" dfmt) + ("Dart" dart-format) + ("Dhall" dhall) + ("Dockerfile" dockfmt) + ("Elixir" mix-format) + ("Elm" elm-format) + ("Emacs Lisp" emacs-lisp) + ("Erlang" efmt) + ("F#" fantomas) + ("Fish" fish-indent) + ("Fortran Free Form" fprettify) + ("GLSL" clang-format) + ("Go" gofmt) + ("GraphQL" prettier) + ("Haskell" brittany) + ("HTML" html-tidy) + ("Java" clang-format) + ("JavaScript" prettier) + ("JSON" prettier) + ("JSON5" prettier) + ("Jsonnet" jsonnetfmt) + ("JSX" prettier) + ("Kotlin" ktlint) + ("LaTeX" latexindent) + ("Less" prettier) + ("Literate Haskell" brittany) + ("Lua" lua-fmt) + ("Markdown" prettier) + ("Nix" nixpkgs-fmt) + ("Objective-C" clang-format) + ("OCaml" ocp-indent) + ("Perl" perltidy) + ("PHP" prettier) + ("Protocol Buffer" clang-format) + ("PureScript" purty) + ("Python" black) + ("R" styler) + ("Reason" bsrefmt) + ("ReScript" rescript) + ("Ruby" rufo) + ("Rust" rustfmt) + ("Scala" scalafmt) + ("SCSS" prettier) + ("Shell" shfmt) + ("Solidity" prettier) + ("SQL" sqlformat) + ("Svelte" prettier) + ("Swift" swiftformat) + ("Terraform" terraform-fmt) + ("TOML" prettier) + ("TSX" prettier) + ("TypeScript" prettier) + ("V" v-fmt) + ("Verilog" istyle-verilog) + ("Vue" prettier) + ("XML" html-tidy) + ("YAML" prettier) + ("Zig" zig) + + ("_Angular" prettier) + ("_Flow" prettier) + ("_Gleam" gleam) + ("_Ledger" ledger-mode) + ("_Nginx" nginxfmt) + ("_Snakemake" snakefmt)) + "Default formatter to use for each language." + :type '(repeat (list string symbol)) + :group 'format-all) + +(defcustom format-all-show-errors 'errors + "When to show formatting errors or warnings." + :type '(choice (const :tag "Always" always) + (const :tag "Errors" errors) + (const :tag "Warnings" warnings) + (const :tag "Never" never)) + :group 'format-all) + +(defvar format-all-after-format-functions nil + "Hook run after each time `format-all-buffer' has formatted a buffer. + +The value is a list of hook functions. Use `add-hook' to add a +function. The function is called with two arguments: (FORMATTER +STATUS). FORMATTER is a symbol naming the formatter, as given to +`define-format-all-formatter'. STATUS is one of the following +keywords: + +* :reformatted -- The formatter made changes to the buffer. + +* :already-formatted -- The buffer was already formatted + correctly so the formatter didn't make any changes to it. + +* :error -- The formatter encountered an error (usually a syntax + error). The buffer contents are the same as before formatting. + +The current buffer is the buffer that was just formatted. Point +is not guaranteed to be in any particular place, so `goto-char' +before editing the buffer. Narrowing may be in effect unless +STATUS is :reformatted.") + +(defvar format-all--user-args nil + "Internal variable to temporarily store arguments for formatters.") + +(defvar-local format-all-formatters nil + "Rules to select which formatter format-all uses. + +The value is an association list. + +The first item of each association is the name of a programming +language. (GitHub Linguist names are used.) + +The remaining items are one or more formatters to use for that +language. Each formatter is either: + +* a symbol (e.g. black, clang-format, rufo) + +* a list whose first item is that symbol, and any remaining items + are extra command line arguments to pass to the formatter + +If more than one formatter is given for the same language, all of +them are run as a chain, with the code from each formatter passed +to the next. The final code is from the last formatter. In case +any formatter in the chain is missing or fails to format the +code, the entire chain fails and the old code before formatting +is preserved. + +You'll probably want to set this in a \".dir-locals.el\" file or +in a hook function. Any number of buffers can share the same +association list. Using \".dir-locals.el\" is convenient since +the rules for an entire source tree can be given in one file.") + +(define-error 'format-all-executable-not-found + "Formatter command not found") + +(defun format-all--proper-list-p (object) + "Return t if OBJECT is a proper list, nil otherwise." + ;; If we could depend on Emacs 27.1 this function would be built in. + (condition-case _ (not (null (cl-list-length object))) + (wrong-type-argument nil))) + +(defun format-all--normalize-formatter (formatter) + "Internal function to convert FORMATTER spec into normal form." + (let ((formatter (if (listp formatter) formatter (list formatter)))) + (when (cdr (last formatter)) + (error "Formatter is not a proper list: %S" formatter)) + (when (null formatter) + (error "Formatter name missing")) + (unless (symbolp (car formatter)) + (error "Formatter name is not a symbol: %S" (car formatter))) + (unless (cl-every #'stringp (cdr formatter)) + (error "Formatter command line arguments are not all strings: %S" + formatter)) + formatter)) + +(defun format-all--normalize-chain (chain) + "Internal function to convert CHAIN spec into normal form." + (when (or (not (listp chain)) (cdr (last chain))) + (error "Formatter chain is not a proper list: %S" chain)) + (mapcar #'format-all--normalize-formatter chain)) + +(defun format-all-valid-formatters-p (formatters) + "Return t if FORMATTERS is a valid value for `format-all-formatters'." + (and (format-all--proper-list-p formatters) + (cl-every + (lambda (chain) + (and (not (null chain)) + (format-all--proper-list-p chain) + (stringp (car chain)) + (cl-every + (lambda (formatter) + (and (not (null formatter)) + (or (symbolp formatter) + (and (format-all--proper-list-p formatter) + (and (symbolp (car formatter)) + (not (null (car formatter)))) + (cl-every #'stringp (cdr formatter)))))) + (cdr chain)))) + formatters))) + +(put 'format-all-formatters 'safe-local-variable + 'format-all-valid-formatters-p) + +(eval-and-compile + (defconst format-all--system-type + (cl-case system-type + (windows-nt 'windows) + (cygwin 'windows) + (darwin 'macos) + (gnu/linux 'linux) + (berkeley-unix + (save-match-data + (let ((case-fold-search t)) + (cond ((string-match "freebsd" system-configuration) 'freebsd) + ((string-match "openbsd" system-configuration) 'openbsd) + ((string-match "netbsd" system-configuration) 'netbsd)))))) + "Current operating system according to the format-all package.")) + +(eval-and-compile + (defun format-all--resolve-system (choices) + "Get first choice matching `format-all--system-type' from CHOICES." + (cl-dolist (choice choices) + (cond ((atom choice) + (cl-return choice)) + ((eql format-all--system-type (car choice)) + (cl-return (cadr choice))))))) + +(defun format-all--fix-trailing-whitespace () + "Fix trailing whitespace since some formatters don't do that." + (save-match-data + (goto-char (point-min)) + (while (re-search-forward "[ \t]+$" nil t) + (replace-match "")) + (goto-char (point-max)) + (delete-region + (if (re-search-backward "[^ \t\n]" nil t) (match-end 0) (point-min)) + (point-max)) + (unless (= (point-min) (point-max)) + (goto-char (point-max)) + (insert "\n")))) + +(defun format-all--remove-ansi-color (string) + "Internal helper function to remove terminal color codes from STRING." + (save-match-data (replace-regexp-in-string "\x1b\\[[0-9]+m" "" string t))) + +(defun format-all--flatten-once (list) + "Internal helper function to remove nested lists in LIST." + (cl-mapcan (lambda (x) (if (listp x) x (list x))) + list)) + +(defun format-all--buffer-extension-p (&rest extensions) + "Internal helper function to test file name EXTENSIONS." + (and (buffer-file-name) + (save-match-data + (let ((case-fold-search t)) + (cl-some (lambda (ext) + (string-match (concat "\\." (regexp-quote ext) "\\'") + (buffer-file-name))) + extensions))))) + +(defun format-all--buffer-thunk (thunk) + "Internal helper function to implement formatters. + +THUNK is a function that implements a particular formatter. It +takes INPUT (the unformatted source code as a string). THUNK is +invoked such that the current buffer is an empty temp buffer. It +should call the formatter on INPUT and write the formatted source +code output to the temp buffer. It should return (ERRORP +ERROR-OUTPUT). ERRORP is a boolean indicating whether the formatter +caused an error and hence the contents of the temp buffer should +be discarded. ERROR-OUTPUT is a string containing all error/warning +output from the formatter. + +Note that in some cases we can use the output of the formatter +even if it produced warnings. Not all warnings are errors." + (save-excursion + (save-restriction + (widen) + (let ((inbuf (current-buffer)) + (input (buffer-string))) + (inheritenv + (with-temp-buffer + (cl-destructuring-bind (errorp error-output) (funcall thunk input) + (let* ((no-chg (or errorp + (= 0 (let ((case-fold-search nil)) + (compare-buffer-substrings + inbuf nil nil nil nil nil))))) + (output (cond (errorp nil) + (no-chg t) + (t (buffer-string))))) + (list output error-output))))))))) + +(defun format-all--buffer-native (mode &rest funcs) + "Internal helper function to implement formatters. + +In a new temp buffer, switches to MODE then calls FUNCS in order +to format the code. MODE and FUNCS should be symbols instead of +functions to avoid warnings from the Emacs byte compiler." + (format-all--buffer-thunk + (lambda (input) + (funcall mode) + (insert input) + (mapc #'funcall funcs) + (format-all--fix-trailing-whitespace) + (list nil "")))) + +(defun format-all--locate-file (filename) + "Internal helper to locate dominating copy of FILENAME for current buffer." + (let* ((dir (and (buffer-file-name) + (locate-dominating-file (buffer-file-name) filename)))) + (when dir (expand-file-name (concat dir filename))))) + +(defun format-all--locate-default-directory (root-files) + "Internal helper function to find working directory for formatter. + +ROOT-FILES is a list of strings which are the filenames to look +for using `locate-dominating-file'. Details in documentation for +`format-all--buffer-hard'." + (let ((found-dirs + (when (and root-files (buffer-file-name)) + (mapcan (lambda (root-file) + (let ((found-file (locate-dominating-file + (buffer-file-name) root-file))) + (when found-file + (list (file-name-directory found-file))))) + root-files)))) + (or (car (sort found-dirs (lambda (a b) (> (length a) (length b))))) + (and (buffer-file-name) (file-name-directory (buffer-file-name))) + default-directory))) + +(defun format-all--buffer-hard + (ok-statuses error-regexp root-files executable &rest args) + "Internal helper function to implement formatters. + +Runs the external program EXECUTABLE. The program shall read +unformatted code from stdin, write its formatted equivalent to +stdout, and write errors/warnings to stderr. + +The program should exit with status zero for the formatting to be +considered successful. If a list of OK-STATUSES is given, all of +those are actually considered successful. But if ERROR-REGEXP is +given, and the program's stderr contains that regexp, then the +formatting is considered failed even if the exit status is in +OK-STATUSES. OK-STATUSES and ERROR-REGEXP are hacks to work +around formatter programs that don't make sensible use of their +exit status. + +If ARGS are given, those are arguments to EXECUTABLE. They should +not be shell-quoted. + +If ROOT-FILES are given, the working directory of the formatter +will be the deepest directory (starting from the file being +formatted) containing one of these files. If ROOT-FILES is nil, +or none of ROOT-FILES are found in any parent directories, the +working directory will be the one where the formatted file is. +ROOT-FILES is ignored for buffers that are not visiting a file." + (let ((ok-statuses (or ok-statuses '(0))) + (args (append format-all--user-args (format-all--flatten-once args))) + (default-directory (format-all--locate-default-directory root-files))) + (when format-all-debug + (message "Format-All: Running: %s" + (mapconcat #'shell-quote-argument (cons executable args) " ")) + (message "Format-All: Directory: %s" default-directory)) + (format-all--buffer-thunk + (lambda (input) + (let* ((errfile (make-temp-file "format-all-")) + (status (apply #'call-process-region input nil + executable nil (list t errfile) + nil args)) + (error-output (with-temp-buffer + (insert-file-contents errfile) + (delete-file errfile) + (buffer-string))) + (errorp (or (not (member status ok-statuses)) + (and error-regexp + (save-match-data + (string-match error-regexp error-output)))))) + (list errorp error-output)))))) + +(defun format-all--buffer-easy (executable &rest args) + "Internal helper function to implement formatters. + +Runs the external program EXECUTABLE. The program shall read +unformatted code from stdin, write its formatted equivalent to +stdout, write errors/warnings to stderr, and exit zero/non-zero +on success/failure. + +If ARGS are given, those are arguments to EXECUTABLE. They don't +need to be shell-quoted." + (apply 'format-all--buffer-hard nil nil nil executable args)) + +(defun format-all--ruby-gem-bundled-p (gem-name) + "Internal helper function to check for a Ruby gem. + +Returns t if GEM-NAME is listed in the current project's +Gemfile.lock, nil otherwise." + (let* ((lockfile "Gemfile.lock") + (dir (locate-dominating-file (buffer-file-name) lockfile))) + (and dir + (with-temp-buffer + (insert-file-contents (expand-file-name lockfile dir)) + (re-search-forward (format "^ %s " (regexp-quote gem-name)) + nil t)) + t))) + +(defun format-all--buffer-hard-ruby + (gem-name ok-statuses error-regexp root-files executable &rest args) + "Internal helper function to implement ruby based formatters. + +GEM-NAME is the name of a Ruby gem required to run EXECUTABLE. + +For OK-STATUSES, ERROR-REGEXP, ROOT-FILES, EXECUTABLE and ARGS, +see `format-all--buffer-hard'." + (let* ((command (file-name-nondirectory executable)) + (error-regexp + (regexp-opt + (append + (if error-regexp (list error-regexp)) + (list + "Bundler::GemNotFound" + (concat "bundler: failed to load command: " + (regexp-quote command)) + (concat (regexp-opt (list "bundle" (regexp-quote command))) + ": command not found"))))) + (command-args + (append + (if (format-all--ruby-gem-bundled-p gem-name) + (list "bundle" "exec" command) + (list executable)) + (format-all--flatten-once args)))) + (format-all--buffer-hard + ok-statuses error-regexp root-files + (car command-args) + (cdr command-args)))) + +(defvar format-all--executable-table (make-hash-table) + "Internal table of formatter executable names for format-all.") + +(defvar format-all--install-table (make-hash-table) + "Internal table of formatter install commands for format-all.") + +(defvar format-all--language-table (make-hash-table :test 'equal) + "Internal table of major mode formatter lists for format-all.") + +(defvar format-all--features-table (make-hash-table) + "Internal table of formatter feature lists for format-all.") + +(defvar format-all--format-table (make-hash-table) + "Internal table of formatter formatting functions for format-all.") + +(defun format-all--pushhash (key value table) + "Push VALUE onto the list under KEY in hash table TABLE." + (puthash key (cons value (remove value (gethash key table))) table)) + +(defmacro define-format-all-formatter (formatter &rest body) + "Define a new source code formatter for use with format-all. + +FORMATTER is a symbol naming the formatter. The name of the +command used to run the formatter is usually a good choice. + +Consult the existing formatters for examples of BODY." + (let (executable install languages features format) + (cl-assert + (equal (mapcar 'car body) + '(:executable :install :languages :features :format))) + (cl-dolist (part body) + (cl-ecase (car part) + (:executable + (setq executable + (unless (null (cdr part)) + (or (format-all--resolve-system (cdr part)) + (error "Executable not specified for %S system %S" + formatter format-all--system-type))))) + (:install + (setq install (format-all--resolve-system (cdr part)))) + (:languages + (setq languages + (mapcar (lambda (language) + `(format-all--pushhash + ',language ',formatter format-all--language-table)) + (cdr part)))) + (:features + (setq features (cdr part))) + (:format + (setq format `(lambda (executable language region) + (ignore language + ,@(unless executable '(executable)) + ,@(unless (memq 'region features) '(region))) + ,(cadr part)))))) + `(progn (puthash ',formatter ,executable format-all--executable-table) + (puthash ',formatter ,install format-all--install-table) + ,@languages + (puthash ',formatter ',features format-all--features-table) + (puthash ',formatter ,format format-all--format-table) + ',formatter))) + +(define-format-all-formatter asmfmt + (:executable "asmfmt") + (:install) + (:languages "Assembly") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter astyle + (:executable "astyle") + (:install (macos "brew install astyle")) + (:languages "C" "C++" "C#" "Java") + (:features) + (:format (format-all--buffer-easy + executable + (let ((astylerc (format-all--locate-file ".astylerc"))) + (when astylerc (concat "--options=" astylerc)))))) + +(define-format-all-formatter atsfmt + (:executable "atsfmt") + (:install "cabal new-install ats-format --happy-options='-gcsa' -O2") + (:languages "ATS") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter auctex + (:executable) + (:install) + (:languages "LaTeX") + (:features) + (:format (format-all--buffer-native + 'latex-mode + (lambda () + (let ((f (symbol-function 'LaTeX-fill-buffer))) + (when f (funcall f nil))))))) + +(define-format-all-formatter beautysh + (:executable "beautysh") + (:install "pip install beautysh") + (:languages "Shell") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter black + (:executable "black") + (:install "pip install black") + (:languages "Python") + (:features) + (:format (format-all--buffer-easy + executable "-q" + (when (format-all--buffer-extension-p "pyi") "--pyi") + "-"))) + +(define-format-all-formatter brittany + (:executable "brittany") + (:install "stack install brittany") + (:languages "Haskell" "Literate Haskell") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter bsrefmt + (:executable "bsrefmt") + (:install "npm install --global bs-platform") + (:languages "Reason") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter buildifier + (:executable "buildifier") + (:install + (macos "brew install buildifier") + "go get github.com/bazelbuild/buildtools/buildifier") + (:languages "Bazel") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter cabal-fmt + (:executable "cabal-fmt") + (:install "cabal install cabal-fmt") + (:languages "Cabal Config") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter clang-format + (:executable "clang-format") + (:install + (macos "brew install clang-format") + (windows "scoop install llvm")) + (:languages "C" "C#" "C++" "Cuda" "GLSL" "Java" "Objective-C" "Protocol Buffer") + (:features region) + (:format + (format-all--buffer-easy + executable + "-assume-filename" + (or (buffer-file-name) + (cdr (assoc language + '(("C" . ".c") + ("C#" . ".cs") + ("C++" . ".cpp") + ("Cuda" . ".cu") + ("GLSL" . ".glsl") + ("Java" . ".java") + ("Objective-C" . ".m") + ("Protocol Buffer" . ".proto"))))) + (when region + (list "--offset" (number-to-string (1- (car region))) + "--length" (number-to-string (- (cdr region) (car region)))))))) + +(define-format-all-formatter cljfmt + (:executable "cljfmt") + (:install "npm install --global node-cljfmt") + (:languages "Clojure") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter cmake-format + (:executable "cmake-format") + (:install "pip install cmake-format") + (:languages "CMake") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter crystal + (:executable "crystal") + (:install (macos "brew install crystal")) + (:languages "Crystal") + (:features) + (:format (format-all--buffer-easy executable "tool" "format" "-"))) + +(define-format-all-formatter dart-format + (:executable "dart") + (:install (macos "brew tap dart-lang/dart && brew install dart")) + (:languages "Dart") + (:features) + (:format + (format-all--buffer-easy executable "format" "--output" "show"))) + +(define-format-all-formatter dartfmt + (:executable "dartfmt") + (:install (macos "brew tap dart-lang/dart && brew install dart")) + (:languages "Dart") + (:features) + (:format + (format-all--buffer-easy + executable + (when (buffer-file-name) + (list "--stdin-name" (buffer-file-name)))))) + +(define-format-all-formatter dfmt + (:executable "dfmt") + (:install (macos "brew install dfmt")) + (:languages "D") + (:features) + (:format + (format-all--buffer-hard nil (regexp-quote "[error]") nil executable))) + +(define-format-all-formatter dhall + (:executable "dhall") + (:install (macos "brew install dhall")) + (:languages "Dhall") + (:features) + (:format (format-all--buffer-easy executable "format"))) + +(define-format-all-formatter dockfmt + (:executable "dockfmt") + (:install "go get github.com/jessfraz/dockfmt") + (:languages "Dockerfile") + (:features) + (:format (format-all--buffer-easy executable "fmt"))) + +(define-format-all-formatter efmt + (:executable "efmt") + (:install "cargo install efmt") + (:languages "Erlang") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter elm-format + (:executable "elm-format") + (:install (macos "brew install elm")) + (:languages "Elm") + (:features) + (:format + (cl-destructuring-bind (output error-output) + (format-all--buffer-hard nil nil '("elm.json" "elm-package.json") + executable "--yes" "--stdin") + (let ((error-output (format-all--remove-ansi-color error-output))) + (list output error-output))))) + +(define-format-all-formatter emacs-bibtex + (:executable) + (:install) + (:languages "BibTeX") + (:features) + (:format (format-all--buffer-native 'bibtex-mode 'bibtex-reformat))) + +(define-format-all-formatter emacs-bibtex-sort + (:executable) + (:install) + (:languages "BibTeX") + (:features) + (:format (format-all--buffer-native 'bibtex-mode 'bibtex-sort-buffer))) + +(define-format-all-formatter emacs-lisp + (:executable) + (:install) + (:languages "Emacs Lisp") + (:features region) + (:format + (format-all--buffer-native + 'emacs-lisp-mode + (if region + (lambda () (indent-region (car region) (cdr region))) + (lambda () (indent-region (point-min) (point-max))))))) + +(define-format-all-formatter fantomas + (:executable "fantomas") + (:install "dotnet tool install -g fantomas-tool") + (:languages "F#") + (:features) + (:format (format-all--buffer-easy executable "--stdin" "--stdout"))) + +(define-format-all-formatter fish-indent + (:executable "fish_indent") + (:install (macos "brew install fish OR port install fish")) + (:languages "Fish") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter fourmolu + (:executable "fourmolu") + (:install "stack install fourmolu") + (:languages "Haskell" "Literate Haskell") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter fprettify + (:executable "fprettify") + (:install "pip install fprettify") + (:languages "Fortran Free Form") + (:features) + (:format (format-all--buffer-easy executable "--silent"))) + +(define-format-all-formatter gawk + (:executable "gawk") + (:install (macos "brew install gawk")) + (:languages "Awk") + (:features) + (:format (format-all--buffer-easy executable "-f" "-" "--pretty-print=-"))) + +(define-format-all-formatter gleam + (:executable "gleam") + (:install (macos "brew install gleam")) + (:languages "_Gleam") + (:features) + (:format (format-all--buffer-easy executable "format" "--stdin"))) + +(define-format-all-formatter gofmt + (:executable "gofmt") + (:install + (macos "brew install go") + (windows "scoop install go")) + (:languages "Go") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter goimports + (:executable "goimports") + (:install "go get golang.org/x/tools/cmd/goimports") + (:languages "Go") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter hindent + (:executable "hindent") + (:install "stack install hindent") + (:languages "Haskell" "Literate Haskell") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter html-tidy + (:executable "tidy") + (:install + (macos "brew install tidy-html5") + (windows "scoop install tidy")) + (:languages "HTML" "XML") + (:features) + (:format + (format-all--buffer-hard + '(0 1) nil nil + executable + "-q" + "--tidy-mark" "no" + "-indent" + (when (equal language "XML") "-xml")))) + +(define-format-all-formatter isort + (:executable "isort") + (:install "pip install isort") + (:languages "Python") + (:features) + (:format (format-all--buffer-easy executable "-q" "-"))) + +(define-format-all-formatter istyle-verilog + (:executable "iStyle") + (:install) + (:languages "Verilog") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter jsonnetfmt + (:executable "jsonnetfmt") + (:install (macos "brew install jsonnet")) + (:languages "Jsonnet") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter ktlint + (:executable "ktlint") + (:install (macos "brew install ktlint")) + (:languages "Kotlin") + (:features) + (:format (format-all--buffer-easy executable "--format" "--stdin"))) + +(define-format-all-formatter latexindent + (:executable "latexindent") + (:install) + (:languages "LaTeX") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter ledger-mode + (:executable) + (:install) + (:languages "_Ledger") + (:features) + (:format + (format-all--buffer-native 'ledger-mode 'ledger-mode-clean-buffer))) + +(define-format-all-formatter lua-fmt + (:executable "luafmt") + (:install "npm install --global lua-fmt") + (:languages "Lua") + (:features) + (:format (format-all--buffer-easy executable "--stdin"))) + +(define-format-all-formatter mix-format + (:executable "mix") + (:install (macos "brew install elixir")) + (:languages "Elixir") + (:features) + (:format + (format-all--buffer-hard + nil nil '("mix.exs") + executable + "format" + (let ((config-file (format-all--locate-file ".formatter.exs"))) + (when config-file (list "--dot-formatter" config-file))) + "-"))) + +(define-format-all-formatter nginxfmt + (:executable "nginxfmt") + (:install "pip install nginxfmt") + (:languages "_Nginx") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter nixfmt + (:executable "nixfmt") + (:install + "nix-env -f https://github.com/serokell/nixfmt/archive/master.tar.gz -i") + (:languages "Nix") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter nixpkgs-fmt + (:executable "nixpkgs-fmt") + (:install "nix-env -f https://github.com/nix-community/nixpkgs-fmt/archive/master.tar.gz -i") + (:languages "Nix") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter ocp-indent + (:executable "ocp-indent") + (:install "opam install ocp-indent") + (:languages "OCaml") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter ormolu + (:executable "ormolu") + (:install "stack install ormolu") + (:languages "Haskell" "Literate Haskell") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter perltidy + (:executable "perltidy") + (:install "cpan install Perl::Tidy") + (:languages "Perl") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter pgformatter + (:executable "pg_format") + (:install) + (:languages "SQL") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter prettier + (:executable "prettier") + (:install "npm install --global prettier @prettier/plugin-lua @prettier/plugin-php prettier-plugin-solidity prettier-plugin-svelte prettier-plugin-toml") + (:languages + "CSS" "GraphQL" "HTML" "JavaScript" "JSON" "JSON5" "JSX" "Less" "Lua" + "Markdown" "PHP" "SCSS" "Solidity" "Svelte" "TOML" "TSX" "TypeScript" + "Vue" "YAML" + "_Angular" "_Flow") + (:features region) + (:format + (format-all--buffer-easy + executable + (unless (buffer-file-name) + (list "--parser" + (let ((pair (assoc language + '(("_Angular" . "angular") + ("_Flow" . "flow") + ("JavaScript" . "babel") + ("JSX" . "babel") + ("Solidity" . "solidity-parse") + ("TSX" . "typescript"))))) + (if pair (cdr pair) (downcase language))))) + (when (buffer-file-name) + (list "--stdin-filepath" (buffer-file-name))) + (let ((ignore-file (format-all--locate-file ".prettierignore"))) + (when ignore-file + (list "--ignore-path" ignore-file))) + (when region + (list "--range-start" (number-to-string (1- (car region))) + "--range-end" (number-to-string (1- (cdr region)))))))) + +(define-format-all-formatter purs-tidy + (:executable "purs-tidy") + (:install "npm install --global purs-tidy") + (:languages "PureScript") + (:features) + (:format (format-all--buffer-easy executable "format"))) + +(define-format-all-formatter purty + (:executable "purty") + (:install "npm install --global purty") + (:languages "PureScript") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter raco-fmt + (:executable "raco") + (:install "raco pkg install fmt") + (:languages "Racket") + (:features) + (:format (format-all--buffer-easy executable "fmt"))) + +(define-format-all-formatter rescript + (:executable "rescript") + (:install "npm install --global rescript") + (:languages "ReScript") + (:features) + (:format + (format-all--buffer-easy + executable "format" "-stdin" + (let ((ext (if (not (buffer-file-name)) "" + (file-name-extension (buffer-file-name))))) + (concat "." (if (equal ext "") "res" ext)))))) + +(define-format-all-formatter rubocop + (:executable "rubocop") + (:install "gem install rubocop:'>=1.4.0'") + (:languages "Ruby") + (:features) + (:format + (format-all--buffer-hard-ruby + "rubocop" '(0 1) nil nil + executable + "--auto-correct" + "--format" "quiet" + "--stderr" + "--stdin" (or (buffer-file-name) (buffer-name))))) + +(define-format-all-formatter rufo + (:executable "rufo") + (:install "gem install rufo") + (:languages "Ruby") + (:features) + (:format + (format-all--buffer-hard-ruby + "rufo" nil nil nil + executable + "--simple-exit" + (when (buffer-file-name) + (list "--filename" (buffer-file-name)))))) + +(define-format-all-formatter rustfmt + (:executable "rustfmt") + (:install "rustup component add rustfmt") + (:languages "Rust") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter scalafmt + (:executable "scalafmt") + (: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") + (:languages "Scala") + (:features) + (:format + (format-all--buffer-easy + executable "--stdin" "--non-interactive" "--quiet"))) + +(define-format-all-formatter shfmt + (:executable "shfmt") + (:install + (macos "brew install shfmt") + (windows "scoop install shfmt")) + (:languages "Shell") + (:features) + (:format + (format-all--buffer-easy + executable + (if (buffer-file-name) + (list "-filename" (buffer-file-name)) + (list "-ln" (cl-case (and (eql major-mode 'sh-mode) + (boundp 'sh-shell) + (symbol-value 'sh-shell)) + (bash "bash") + (mksh "mksh") + (t "posix"))))))) + +(define-format-all-formatter snakefmt + (:executable "snakefmt") + (:install) + (:languages "_Snakemake") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter sqlformat + (:executable "sqlformat") + (:install "pip install sqlparse") + (:languages "SQL") + (:features) + (:format + (let* ((ic (car default-process-coding-system)) + (oc (cdr default-process-coding-system)) + (ienc (symbol-name (or (coding-system-get ic :mime-charset) + 'utf-8))) + (oenc (symbol-name (or (coding-system-get oc :mime-charset) + 'utf-8))) + (process-environment (cons (concat "PYTHONIOENCODING=" oenc) + process-environment))) + (format-all--buffer-easy executable "--encoding" ienc "-")))) + +(define-format-all-formatter standard + (:executable "standard") + (:install "npm install --global standard") + (:languages "JavaScript" "JSX") + (:features) + (:format + ;; `standard --stdin` properly uses zero vs non-zero exit codes to + ;; indicate success vs error. However, it checks for quite a broad + ;; range of errors, all the way up to undeclared identifiers and + ;; such. To catch only syntax errors, we need to look specifically + ;; for the text "Parsing error:". + (format-all--buffer-hard + '(0 1) ".*?:.*?:[0-9]+:[0-9]+: Parsing error:" nil + executable "--fix" "--stdin"))) + +(define-format-all-formatter standardrb + (:executable "standardrb") + (:install "gem install standard:'>=0.13.0'") + (:languages "Ruby") + (:features) + (:format + (format-all--buffer-hard-ruby + "standard" '(0 1) nil nil + executable + "--stderr" + "--fix" + "--stdin" (or (buffer-file-name) (buffer-name))))) + +(define-format-all-formatter styler + (:executable "Rscript") + (:install "Rscript -e \"install.packages('styler')\"") + (:languages "R") + (:features) + (:format + (format-all--buffer-easy + executable + "-e" (concat + "options(styler.colored_print.vertical=FALSE);" + " con <- file('stdin');" + " out <- styler::style_text(readLines(con));" + " close(con);" + " out")))) + +(define-format-all-formatter stylish-haskell + (:executable "stylish-haskell") + (:install "stack install stylish-haskell") + (:languages "Haskell") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter stylua + (:executable "stylua") + (:install "cargo install stylua") + (:languages "Lua") + (:features) + (:format (format-all--buffer-easy executable "-"))) + +(define-format-all-formatter swiftformat + (:executable "swiftformat") + (:install (macos "brew install swiftformat")) + (:languages "Swift") + (:features region) + (:format + (format-all--buffer-easy + executable + "--quiet" + (let ((config (format-all--locate-file ".swiftformat"))) + (when config (list "--config" config))) + (when region + (list "--linerange" (format "%d,%d" + (line-number-at-pos (car region)) + (line-number-at-pos (cdr region)))))))) + +(define-format-all-formatter terraform-fmt + (:executable "terraform") + (:install (macos "brew install terraform")) + (:languages "Terraform") + (:features) + (:format (format-all--buffer-easy executable "fmt" "-no-color" "-"))) + +(define-format-all-formatter ts-standard + (:executable "ts-standard") + (:install "npm install --global ts-standard") + (:languages "TypeScript" "TSX") + (:features) + (:format + ;; `ts-standard --stdin` properly uses zero vs non-zero exit codes to + ;; indicate success vs error. However, it checks for quite a broad + ;; range of errors, all the way up to undeclared identifiers and + ;; such. To catch only syntax errors, we need to look specifically + ;; for the text "Parsing error:". + (format-all--buffer-hard + '(0 1) ".*?:.*?:[0-9]+:[0-9]+: Parsing error:" '("tsconfig.json") + executable "--fix" "--stdin" + (when (buffer-file-name) + (list "--stdin-filename" (buffer-file-name)))))) + +(define-format-all-formatter v-fmt + (:executable "v") + (:install) + (:languages "V") + (:features) + (:format (format-all--buffer-easy executable "fmt"))) + +(define-format-all-formatter yapf + (:executable "yapf") + (:install "pip install yapf") + (:languages "Python") + (:features) + (:format (format-all--buffer-easy executable))) + +(define-format-all-formatter zig + (:executable "zig") + (:install) + (:languages "Zig") + (:features) + (:format (format-all--buffer-easy executable "fmt" "--stdin"))) + +(define-format-all-formatter zprint + (:executable "zprint") + (:install) + (:languages "Clojure") + (:features) + (:format (format-all--buffer-easy executable))) + +(defun format-all--language-id-buffer () + "Return the language used in the current buffer, or NIL. + +Prefer getting the ID from the language-id library. Some +languages do not yet have official GitHub Linguist identifiers, +yet format-all needs to know about them anyway. That's why we +have this custom language-id function in format-all. The +unofficial languages IDs are prefixed with \"_\"." + (or (and (or (equal major-mode 'angular-html-mode) + (and (equal major-mode 'web-mode) + (equal (symbol-value 'web-mode-content-type) "html") + (equal (symbol-value 'web-mode-engine) "angular"))) + "_Angular") + (and (member major-mode '(js-mode js2-mode js3-mode)) + (boundp 'flow-minor-mode) + (not (null (symbol-value 'flow-minor-mode))) + "_Flow") + (and (equal major-mode 'gleam-mode) "_Gleam") + (and (equal major-mode 'ledger-mode) "_Ledger") + (and (equal major-mode 'nginx-mode) "_Nginx") + (and (equal major-mode 'snakemake-mode) "_Snakemake") + (language-id-buffer))) + +(defun format-all--please-install (executable installer) + "Internal helper function for error about missing EXECUTABLE and INSTALLER." + (concat (format "You need the %s command." executable) + (if (not installer) "" + (format " You may be able to install it via: %s" installer)))) + +(defun format-all--formatter-executable (formatter) + "Internal helper function to get the external program for FORMATTER." + (let ((executable (gethash formatter format-all--executable-table))) + (when executable + (or (executable-find executable) + (signal 'format-all-executable-not-found + (list (format-all--please-install + executable + (gethash formatter format-all--install-table)))))))) + +(defun format-all--show-errors-buffer (error-output show-errors-p) + "Internal shorthand function to update and show error output. + +ERROR-OUTPUT come from the formatter. SHOW-ERRORS-P determines +whether or not to display the errors buffer." + (save-selected-window + (with-current-buffer (get-buffer-create "*format-all-errors*") + (erase-buffer) + (insert error-output) + (if show-errors-p + (display-buffer (current-buffer)) + (let ((error-window (get-buffer-window (current-buffer)))) + (when error-window (quit-window nil error-window))))))) + +(defun format-all--update-errors-buffer (status error-output) + "Internal helper function to update *format-all-errors*. + +STATUS and ERROR-OUTPUT come from the formatter." + (let* ((has-warnings-p (not (= 0 (length error-output)))) + (has-errors-p (eq status :error)) + (show-errors-p (cl-case format-all-show-errors + (never nil) + (always t) + (warnings (or has-errors-p has-warnings-p)) + (errors has-errors-p)))) + (format-all--show-errors-buffer error-output show-errors-p))) + +(defun format-all--save-line-number (thunk) + "Internal helper function to run THUNK and go back to the same line." + (let ((old-line-number (line-number-at-pos)) + (old-column (current-column))) + (funcall thunk) + (goto-char (point-min)) + (forward-line (1- old-line-number)) + (let ((line-length (- (point-at-eol) (point-at-bol)))) + (goto-char (+ (point) (min old-column line-length)))))) + +(defun format-all--run-chain (language chain region) + "Internal function to run a formatter CHAIN on the current buffer. + +LANGUAGE is the language ID of the current buffer, from +`format-all--language-id-buffer`. + +REGION is either a (START . END) pair, or nil to format the +entire buffer." + (let* ((chain (format-all--normalize-chain chain)) + (chain-tail chain) + (error-output "") + (reformatted-by '())) + (let ((unsupported + (when region + (cl-remove-if + (lambda (f-name) + (memq 'region (gethash f-name format-all--features-table))) + (mapcar #'car chain))))) + (when unsupported + (error "The format-all-region command is not supported for %s" + (string-join (mapcar #'symbol-name unsupported) ", ")))) + (format-all--save-line-number + (lambda () + (cl-loop + (unless (and chain-tail (= 0 (length error-output))) + (cl-return)) + (let* ((formatter (car chain-tail)) + (f-name (car formatter)) + (f-args (cdr formatter)) + (f-function (gethash f-name format-all--format-table)) + (f-executable (format-all--formatter-executable f-name))) + (when format-all-debug + (message + "Format-All: Formatting %s as %s using %S%s" + (buffer-name) language f-name + (with-temp-buffer + (dolist (arg f-args) (insert " " (shell-quote-argument arg))) + (buffer-string)))) + (cl-destructuring-bind (f-output f-error-output) + (let ((format-all--user-args f-args)) + (funcall f-function f-executable language region)) + (let ((f-status :already-formatted)) + (cond ((null f-output) + (setq error-output f-error-output) + (setq f-status :error)) + ((not (equal f-output t)) + (setq reformatted-by + (append reformatted-by (list f-name))) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert f-output)) + (setq f-status :reformatted))) + (run-hook-with-args 'format-all-after-format-functions + f-name f-status) + (format-all--update-errors-buffer f-status f-error-output)))) + (setq chain-tail (cdr chain-tail))) + (message "%s" + (cond ((not (= 0 (length error-output))) "Formatting error") + ((not reformatted-by) "Already formatted") + (t "Reformatted!"))))))) + +(defun format-all--get-default-chain (language) + "Internal function to get the default formatter chain for LANGUAGE." + (when language (cdr (assoc language format-all-default-formatters)))) + +(defun format-all--get-chain (language) + "Internal function to get LANGUAGE formatter chain for current buffer." + (when language (cdr (assoc language format-all-formatters)))) + +(defun format-all--set-chain (language chain) + "Internal function to set LANGUAGE formatter CHAIN for current buffer." + (cl-assert (stringp language)) + (cl-assert (listp chain)) + (setq format-all-formatters + (append (cl-remove-if (lambda (pair) (equal language (car pair))) + format-all-formatters) + (when chain (list (cons language chain)))))) + +(defun format-all--prompt-for-formatter (language) + "Internal function to choose a formatter for LANGUAGE." + (let ((f-names (gethash language format-all--language-table))) + (cond ((null f-names) + (error "No supported formatters for %s" + (or language "this language"))) + ((null (cdr f-names)) + (car f-names)) + (t + (let ((f-string (completing-read + (format "Formatter for %s: " language) + (mapcar #'list f-names) nil t))) + (and (not (= 0 (length f-string))) + (intern f-string))))))) + +(defun format-all--buffer-from-hook () + "Internal helper function to auto-format current buffer from a hook. + +Format-All installs this function into `before-save-hook' to +format buffers on save. This is a lenient version of +`format-all-buffer' that silently succeeds instead of signaling +an error if the current buffer has no formatter." + (let ((language (format-all--language-id-buffer))) + (format-all--run-chain language + (format-all--get-chain language) + nil))) + +(defun format-all--buffer-or-region (prompt region) + "Internal helper function to auto-format current buffer from command. + +If PROMPT is non-nil, prompt interactively for formatter. +If REGION is non-nil, it is a (START . END) pair passed to the formatter." + (let* ((language (format-all--language-id-buffer)) + (chain (format-all--get-chain language))) + (when (or (equal 'always prompt) (and prompt (not chain))) + (let ((f-name (format-all--prompt-for-formatter language))) + (when f-name + (message "Setting formatter to %S" f-name) + (setq chain (list f-name)) + (format-all--set-chain language chain)))) + (unless chain (error "No formatter")) + (format-all--run-chain language chain region))) + +;;;###autoload +(defun format-all-buffer (&optional prompt) + "Auto-format the source code in the current buffer. + +No disk files are touched - the buffer doesn't even need to be +saved. If you don't like the results of the formatting, you can +use ordinary undo to get your code back to its previous state. + +You will need to install external programs to do the formatting. +If the command can't find the program that it needs, it will try +to tell you how you might be able to install it on your operating +system. Only BibTeX, Emacs Lisp and Ledger are formatted without an +external program. + +A suitable formatter is selected according to the `major-mode' of +the buffer. Many popular programming languages are supported. +It is fairly easy to add new languages that have an external +formatter. When called interactively or PROMPT-P is non-nil, a +missing formatter is prompted in the minibuffer. + +If PROMPT is non-nil (or the function is called as an interactive +command), a missing formatter is prompted in the minibuffer. If +PROMPT is the symbol `always' (or a prefix argument is given), +the formatter is prompted for even if one has already been set. + +If any errors or warnings were encountered during formatting, +they are shown in a buffer called *format-all-errors*." + (interactive (list (if current-prefix-arg 'always t))) + (format-all--buffer-or-region prompt nil)) + +;;;###autoload +(defun format-all-region (start end &optional prompt) + "Auto-format the source code in the current region. + +Like `format-all-buffer' but format only the active region +instead of the entire buffer. This requires support from the +formatter. + +Called non-interactively, START and END delimit the region. +The PROMPT argument works as for `format-all-buffer'." + (interactive + (let ((prompt (if current-prefix-arg 'always t))) + (if (use-region-p) + (list (region-beginning) (region-end) prompt) + (error "The region is not active now")))) + (format-all--buffer-or-region prompt (cons start end))) + +(defun format-all-ensure-formatter () + "Ensure current buffer has a formatter, using default if not." + (interactive) + (let ((language (format-all--language-id-buffer))) + (unless (format-all--get-chain language) + (cond ((not language) + (message "No formatter for this language")) + ((not (gethash language format-all--language-table)) + (message "No formatter for %s" language)) + (t + (let ((default (format-all--get-default-chain language))) + (cond ((not default) + (message "No default formatter for %s" language)) + (t + (message "Using default formatter%s" + (with-temp-buffer + (dolist (formatter default (buffer-string)) + (insert (format " %S" formatter))))) + (format-all--set-chain language default))))))))) + +;;;###autoload +(define-minor-mode format-all-mode + "Toggle automatic source code formatting before save. + +When this minor mode (FmtAll) is enabled, `format-all-buffer' is +automatically called to format your code each time before you +save the buffer. + +The mode is buffer-local and needs to be enabled separately each +time a file is visited. You may want to use `add-hook' in your +`user-init-file' to enable the mode based on buffer modes. E.g.: + + (add-hook 'prog-mode-hook 'format-all-mode) + +To use a default formatter for projects that don't have one, add +this too: + + (add-hook 'prog-mode-hook 'format-all-ensure-formatter) + +When `format-all-mode' is called as a Lisp function, the mode is +toggled if ARG is 鈥榯oggle鈥, disabled if ARG is a negative integer +or zero, and enabled otherwise." + :lighter " FmtAll" + :global nil + (if format-all-mode + (progn + (add-hook 'before-save-hook + 'format-all--buffer-from-hook + nil 'local) + ) + (remove-hook 'before-save-hook + 'format-all--buffer-from-hook + 'local))) + +(provide 'format-all) + +;;; format-all.el ends here diff --git a/elisp/icons/close.xpm b/elisp/icons/close.xpm new file mode 100755 index 0000000..4f3c148 --- /dev/null +++ b/elisp/icons/close.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static char * close_xpm[] = { +"16 22 15 1", +" c None", +". c #848400", +"+ c #E7E79C", +"@ c #E7E794", +"# c #DEDE8C", +"$ c #ADAD39", +"% c #ADAD42", +"& c #B5B54A", +"* c #B5B552", +"= c #BDBD5A", +"- c #9C9C29", +"; c #A5A531", +"> c #C6C663", +", c #C6C66B", +"' c #CECE73", +" ", +" ", +" ", +" ", +" ", +" ", +" .... ", +".+@@#....... ", +".....$%%&&*=. ", +".-;;$%%&&*==. ", +".;;$%%&&*===. ", +".;$%%&&*===>. ", +".$%%&&*===>,. ", +".%%&&*===>,,. ", +".%&&*===>,,'. ", +"............. ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/elisp/icons/leaf.xpm b/elisp/icons/leaf.xpm new file mode 100755 index 0000000..f1d234d --- /dev/null +++ b/elisp/icons/leaf.xpm @@ -0,0 +1,34 @@ +/* XPM */ +static char * leaf_xpm[] = { +"16 22 9 1", +" c None", +". c #424242", +"+ c #949494", +"@ c #9C9C9C", +"# c #A5A5A5", +"$ c #ADADAD", +"% c #848484", +"& c #B5B5B5", +"* c #BDBDBD", +" ", +" ", +" ", +" ", +" ", +"....... ", +".++@@#.. ", +".+@@##.$. ", +".@@###.... ", +".@%%%+%$&. ", +".###$$$&&. ", +".#%+%%%&*. ", +".#$$$&&**. ", +".$%%+%%**. ", +".$$&&****. ", +".$&&*****. ", +".......... ", +" ", +" ", +" ", +" ", +" "}; diff --git a/elisp/icons/open.xpm b/elisp/icons/open.xpm new file mode 100755 index 0000000..39d4968 --- /dev/null +++ b/elisp/icons/open.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char * open_xpm[] = { +"16 22 14 1", +" c None", +". c #848400", +"+ c #D6D67B", +"@ c #CECE7B", +"# c #CECE73", +"$ c #C6C66B", +"% c #BDBD5A", +"& c #BDBD52", +"* c #ADAD39", +"= c #ADAD42", +"- c #B5B54A", +"; c #C6C663", +"> c #CECE6B", +", c #A5A5C6", +" ", +" ", +" ", +" ", +" ", +" ", +" .... ", +".+@@#..... ", +".@@##$$%%&. ", +".@#.......... ", +".#.*==--&%%;;. ", +".#.==--&%%;;. ", +"..==--&%%;;$. ", +"..=--&%%;;$. ", +".=--&%%;;$>. ", +"........... ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/elisp/inheritenv.el b/elisp/inheritenv.el new file mode 100755 index 0000000..6e3b8df --- /dev/null +++ b/elisp/inheritenv.el @@ -0,0 +1,89 @@ +;;; inheritenv.el --- Make temp buffers inherit buffer-local environment variables -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Steve Purcell + +;; Author: Steve Purcell +;; URL: https://github.com/purcell/inheritenv +;; Package-Version: 20210204.354 +;; Package-Commit: c2c879acf89682559b157fb069e1da008f4912ea +;; Package-Requires: ((emacs "24.4")) +;; Version: 0.1-pre +;; Keywords: unix + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Environment variables in Emacs can be set buffer-locally, like many +;; Emacs preferences, which allows users to have different buffer-local +;; paths for executables in different projects, specified by a +;; ".dir-locals.el" file or via a "direnv" integration like +;; envrc (see https://github.com/purcell/envrc). + +;; However, there's a fairly common pitfall when Emacs libraries run +;; background processes on behalf of a user: many such libraries run +;; processes in temporary buffers that do not inherit the calling +;; buffer's environment. This can result in executables not being found, +;; or the wrong versions of executables being picked up. + +;; An example is the Emacs built-in command +;; `shell-command-to-string'. Whatever buffer-local `process-environment' +;; (or `exec-path') the user has set, that command will always use the +;; Emacs-wide default. This is *specified* behaviour, but not *expected* +;; or *helpful*. + +;; `inheritenv' provides a couple of tools for dealing with this +;; issue: + +;; 1. Library authors can wrap code that plans to execute processes in +;; temporary buffers with the `inheritenv' macro. +;; 2. End users can modify commands like `shell-command-to-string' using +;; the `inheritenv-add-advice' macro. + +;;; Code: + +(require 'cl-lib) + +;;;###autoload +(defun inheritenv-apply (func &rest args) + "Apply FUNC such that the environment it sees will match the current value. +This is useful if FUNC creates a temp buffer, because that will +not inherit any buffer-local values of variables `exec-path' and +`process-environment'. + +This function is designed for convenient use as an \"around\" advice. + +ARGS is as for ORIG." + (cl-letf* (((default-value 'process-environment) process-environment) + ((default-value 'exec-path) exec-path)) + (apply func args))) + + +(defmacro inheritenv (&rest body) + "Wrap BODY so that the environment it sees will match the current value. +This is useful if BODY creates a temp buffer, because that will +not inherit any buffer-local values of variables `exec-path' and +`process-environment'." + `(inheritenv-apply (lambda () ,@body))) + + +(defmacro inheritenv-add-advice (func) + "Advise function FUNC with `inheritenv-apply'. +This will ensure that any buffers (including temporary buffers) +created by FUNC will inherit the caller's environment." + `(advice-add ,func :around 'inheritenv-apply)) + + +(provide 'inheritenv) +;;; inheritenv.el ends here diff --git a/elisp/language-id.el b/elisp/language-id.el new file mode 100755 index 0000000..5330a9a --- /dev/null +++ b/elisp/language-id.el @@ -0,0 +1,276 @@ +;;; language-id.el --- Library to work with programming language identifiers -*- lexical-binding: t -*- + +;; Author: Lassi Kortela +;; URL: https://github.com/lassik/emacs-language-id +;; Package-Version: 20220613.1156 +;; Package-Commit: cbd5b8fffa8c09f86d8b77f8d3f5e6f4ccd82bc4 +;; Version: 0.19 +;; Package-Requires: ((emacs "24.3")) +;; Keywords: languages util +;; SPDX-License-Identifier: ISC + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; language-id is a small, focused library that helps other Emacs +;; packages identify the programming languages and markup languages +;; used in Emacs buffers. The main point is that it contains an +;; evolving table of language definitions that doesn't need to be +;; replicated in other packages. + +;; Right now there is only one public function, `language-id-buffer'. +;; It looks at the major mode and other variables and returns the +;; language's GitHub Linguist identifier. We can add support for +;; other kinds of identifiers if there is demand. + +;; This library does not do any statistical text matching to guess the +;; language. + +;;; Code: + +(require 'cl-lib) + +(defvar language-id--file-name-extension nil + "Internal variable for file name extension during lookup.") + +;; +(defconst language-id--definitions + '( + + ;;; Definitions that need special attention to precedence order. + + ;; It is not uncommon for C++ mode to be used when writing Cuda. + ;; In this case, the only way to correctly identify Cuda is by + ;; looking at the extension. + ("Cuda" + (c++-mode + (language-id--file-name-extension ".cu")) + (c++-mode + (language-id--file-name-extension ".cuh"))) + + ;; json-mode is derived from javascript-mode. + ("JSON5" + (json-mode + (language-id--file-name-extension ".json5")) + (web-mode + (web-mode-content-type "json") + (web-mode-engine "none") + (language-id--file-name-extension ".json5"))) + ("JSON" + json-mode + jsonian-mode + (web-mode + (web-mode-content-type "json") + (web-mode-engine "none"))) + + ;; php-mode is derived from c-mode. + ("PHP" php-mode) + + ;; scss-mode is derived from css-mode. + ("SCSS" scss-mode) + + ;; solidity-mode is derived from c-mode. + ("Solidity" solidity-mode) + + ;; svelte-mode is derived from html-mode. + ("Svelte" + svelte-mode + (web-mode + (web-mode-content-type "html") + (web-mode-engine "svelte"))) + + ;; terraform-mode is derived from hcl-mode. + ("Terraform" terraform-mode) + + ;; TypeScript/TSX need to come before JavaScript/JSX because in + ;; web-mode we can tell them apart by file name extension only. + ;; + ;; This implies that we inconsistently classify unsaved temp + ;; buffers using TypeScript/TSX as JavaScript/JSX. + ("TSX" + typescript-tsx-mode + (web-mode + (web-mode-content-type "jsx") + (web-mode-engine "none") + (language-id--file-name-extension ".tsx"))) + ("TypeScript" + typescript-mode + (web-mode + (web-mode-content-type "javascript") + (web-mode-engine "none") + (language-id--file-name-extension ".ts"))) + + ;; ReScript needs to come before Reason because in reason-mode + ;; we can tell them apart by file name extension only. + ("ReScript" + (reason-mode + (language-id--file-name-extension ".res"))) + ("ReScript" + (reason-mode + (language-id--file-name-extension ".resi"))) + ("Reason" reason-mode) + + ;; vue-html-mode is derived from html-mode. + ("Vue" + vue-mode + vue-html-mode + (web-mode + (web-mode-content-type "html") + (web-mode-engine "vue"))) + + ;;; The rest of the definitions are in alphabetical order. + + ("Assembly" asm-mode nasm-mode) + ("ATS" ats-mode) + ("Awk" awk-mode) + ("Bazel" bazel-mode) + ("BibTeX" bibtex-mode) + ("C" c-mode) + ("C#" csharp-mode) + ("C++" c++-mode) + ("Cabal Config" haskell-cabal-mode) + ("Clojure" clojurescript-mode clojurec-mode clojure-mode) + ("CMake" cmake-mode) + ("Common Lisp" lisp-mode) + ("Crystal" crystal-mode) + ("CSS" + css-mode + (web-mode + (web-mode-content-type "css") + (web-mode-engine "none"))) + ("Cuda" cuda-mode) + ("D" d-mode) + ("Dart" dart-mode) + ("Dhall" dhall-mode) + ("Dockerfile" dockerfile-mode) + ("EJS" + (web-mode + (web-mode-content-type "html") + (web-mode-engine "ejs"))) + ("Elixir" elixir-mode) + ("Elm" elm-mode) + ("Emacs Lisp" emacs-lisp-mode) + ("Erlang" erlang-mode) + ("F#" fsharp-mode) + ("Fish" fish-mode) + ("Fortran" fortran-mode) + ("Fortran Free Form" f90-mode) + ("GLSL" glsl-mode) + ("Go" go-mode) + ("GraphQL" graphql-mode) + ("Haskell" haskell-mode) + ("HCL" hcl-mode) + ("HTML" + html-helper-mode + mhtml-mode + html-mode + nxhtml-mode + (web-mode + (web-mode-content-type "html") + (web-mode-engine "none"))) + ("Java" java-mode) + ("JavaScript" + (js-mode + (flow-minor-mode nil)) + (js2-mode + (flow-minor-mode nil)) + (js3-mode + (flow-minor-mode nil)) + (web-mode + (web-mode-content-type "javascript") + (web-mode-engine "none"))) + ("JSON" + json-mode + (web-mode + (web-mode-content-type "json") + (web-mode-engine "none"))) + ("Jsonnet" jsonnet-mode) + ("JSX" + js2-jsx-mode + jsx-mode + rjsx-mode + react-mode + (web-mode + (web-mode-content-type "jsx") + (web-mode-engine "none"))) + ("Kotlin" kotlin-mode) + ("LaTeX" latex-mode) + ("Less" less-css-mode) + ("Literate Haskell" literate-haskell-mode) + ("Lua" lua-mode) + ("Markdown" gfm-mode markdown-mode) + ("Nix" nix-mode) + ("Objective-C" objc-mode) + ("OCaml" caml-mode tuareg-mode) + ("Perl" cperl-mode perl-mode) + ("Protocol Buffer" protobuf-mode) + ("PureScript" purescript-mode) + ("Python" python-mode) + ("R" + ess-r-mode + (ess-mode + (ess-dialect "R"))) + ("Racket" racket-mode) + ("Ruby" enh-ruby-mode ruby-mode) + ("Rust" rust-mode rustic-mode) + ("Scala" scala-mode) + ("Scheme" scheme-mode) + ("Shell" sh-mode) + ("SQL" sql-mode) + ("Swift" swift-mode swift3-mode) + ("TOML" toml-mode conf-toml-mode) + ("V" v-mode) + ("Verilog" verilog-mode) + ("XML" + nxml-mode + xml-mode + (web-mode + (web-mode-content-type "xml") + (web-mode-engine "none"))) + ("YAML" yaml-mode) + ("Zig" zig-mode)) + "Internal table of programming language definitions.") + +(defun language-id--mode-match-p (mode) + "Interal helper to match current buffer against MODE." + (let ((mode (if (listp mode) mode (list mode)))) + (cl-destructuring-bind (wanted-major-mode &rest variables) mode + (and (derived-mode-p wanted-major-mode) + (cl-every + (lambda (variable) + (cl-destructuring-bind (symbol wanted-value) variable + (equal wanted-value + (if (boundp symbol) (symbol-value symbol) nil)))) + variables))))) + +(defun language-id-buffer () + "Get GitHub Linguist language name for current buffer. + +Return the name of the programming language or markup language +used in the current buffer. The name is a string from the GitHub +Linguist language list. The language is determined by looking at +the active `major-mode'. Some major modes support more than one +language. In that case minor modes and possibly other variables +are consulted to disambiguate the language. + +In addition to the modes bundled with GNU Emacs, many third-party +modes are recognized. No statistical text matching or other +heuristics are used in detecting the language. + +The language definitions live inside the language-id library and +are updated in new releases of the library. + +If the language is not unambiguously recognized, the function +returns nil." + (let ((language-id--file-name-extension + (downcase (file-name-extension (or (buffer-file-name) "") t)))) + (cl-some (lambda (definition) + (cl-destructuring-bind (language-id &rest modes) definition + (when (cl-some #'language-id--mode-match-p modes) + language-id))) + language-id--definitions))) + +(provide 'language-id) + +;;; language-id.el ends here diff --git a/elisp/loaddefs.el b/elisp/loaddefs.el new file mode 100755 index 0000000..fb71c53 --- /dev/null +++ b/elisp/loaddefs.el @@ -0,0 +1,113 @@ +;;; loaddefs.el --- automatically extracted autoloads +;; +;;; Code: + + +;;;### (autoloads nil "markdown-mode" "markdown-mode.el" (25089 49515 +;;;;;; 0 0)) +;;; Generated autoloads from markdown-mode.el + +(autoload 'markdown-mode "markdown-mode" "\ +Major mode for editing Markdown files. + +\(fn)" t nil) + +(add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode) t) + +(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode) t) + +(autoload 'gfm-mode "markdown-mode" "\ +Major mode for editing GitHub Flavored Markdown files. + +\(fn)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "markdown-mode" '("gfm-" "markdown"))) + +;;;*** + +;;;### (autoloads nil "my-minor-mode" "my-minor-mode.el" (0 0 0 0)) +;;; Generated autoloads from my-minor-mode.el + +(autoload 'my-keys-mode "my-minor-mode" "\ +Custom minor mode mainly to make keybindings. + +If called interactively, enable My-Keys mode if ARG is positive, +and disable it if ARG is zero or negative. If called from Lisp, +also enable the mode if ARG is omitted or nil, and toggle it if +ARG is `toggle'; disable the mode otherwise. + +\(fn &optional ARG)" t nil) + +(put 'global-my-keys-mode 'globalized-minor-mode t) + +(defvar global-my-keys-mode nil "\ +Non-nil if Global My-Keys mode is enabled. +See the `global-my-keys-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `global-my-keys-mode'.") + +(custom-autoload 'global-my-keys-mode "my-minor-mode" nil) + +(autoload 'global-my-keys-mode "my-minor-mode" "\ +Toggle My-Keys mode in all buffers. +With prefix ARG, enable Global My-Keys mode if ARG is positive; +otherwise, disable it. If called from Lisp, enable the mode if +ARG is omitted or nil. + +My-Keys mode is enabled in all buffers where +`(lambda nil (progn (my-keys-mode t) (column-number-mode t)))' would do it. +See `my-keys-mode' for more information on My-Keys mode. + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil "neotree" "neotree.el" (0 0 0 0)) +;;; Generated autoloads from neotree.el + +(autoload 'neotree-find "neotree" "\ +Quick select node which specified PATH in NeoTree. +If path is nil and no buffer file name, then use DEFAULT-PATH, + +\(fn &optional PATH DEFAULT-PATH)" t nil) + +(autoload 'neotree-projectile-action "neotree" "\ +Integration with `Projectile'. + +Usage: + (setq projectile-switch-project-action 'neotree-projectile-action). + +When running `projectile-switch-project' (C-c p p), `neotree' will change root +automatically." t nil) + +(autoload 'neotree-toggle "neotree" "\ +Toggle show the NeoTree window." t nil) + +(autoload 'neotree-show "neotree" "\ +Show the NeoTree window." t nil) + +(autoload 'neotree-hide "neotree" "\ +Close the NeoTree window." t nil) + +(autoload 'neotree-dir "neotree" "\ +Show the NeoTree window, and change root to PATH. + +\(fn PATH)" t nil) + +(defalias 'neotree 'neotree-show "\ +Show the NeoTree window.") + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "neotree" '("neo" "off-p"))) + +;;;*** + +(provide 'loaddefs) +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; loaddefs.el ends here diff --git a/elisp/markdown-mode.el b/elisp/markdown-mode.el new file mode 100755 index 0000000..2162e4f --- /dev/null +++ b/elisp/markdown-mode.el @@ -0,0 +1,9968 @@ +;;; markdown-mode.el --- Major mode for Markdown-formatted text -*- lexical-binding: t; -*- + +;; Copyright (C) 2007-2022 Jason R. Blevins and markdown-mode +;; contributors (see the commit log for details). + +;; Author: Jason R. Blevins +;; Maintainer: Jason R. Blevins +;; Created: May 24, 2007 +;; Version: 2.6-dev +;; Package-Requires: ((emacs "26.1")) +;; Keywords: Markdown, GitHub Flavored Markdown, itex +;; URL: https://jblevins.org/projects/markdown-mode/ + +;; This file is not part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; See the README.md file for details. + + +;;; Code: + +(require 'easymenu) +(require 'outline) +(require 'thingatpt) +(require 'cl-lib) +(require 'url-parse) +(require 'button) +(require 'color) +(require 'rx) +(require 'subr-x) + +(defvar jit-lock-start) +(defvar jit-lock-end) +(defvar flyspell-generic-check-word-predicate) +(defvar electric-pair-pairs) +(defvar sh-ancestor-alist) + +(declare-function project-roots "project") +(declare-function sh-set-shell "sh-script") + + +;;; Constants ================================================================= + +(defconst markdown-mode-version "2.6-dev" + "Markdown mode version number.") + +(defconst markdown-output-buffer-name "*markdown-output*" + "Name of temporary buffer for markdown command output.") + + +;;; Global Variables ========================================================== + +(defvar markdown-reference-label-history nil + "History of used reference labels.") + +(defvar markdown-live-preview-mode nil + "Sentinel variable for command `markdown-live-preview-mode'.") + +(defvar markdown-gfm-language-history nil + "History list of languages used in the current buffer in GFM code blocks.") + + +;;; Customizable Variables ==================================================== + +(defvar markdown-mode-hook nil + "Hook run when entering Markdown mode.") + +(defvar markdown-before-export-hook nil + "Hook run before running Markdown to export XHTML output. +The hook may modify the buffer, which will be restored to it's +original state after exporting is complete.") + +(defvar markdown-after-export-hook nil + "Hook run after XHTML output has been saved. +Any changes to the output buffer made by this hook will be saved.") + +(defgroup markdown nil + "Major mode for editing text files in Markdown format." + :prefix "markdown-" + :group 'text + :link '(url-link "https://jblevins.org/projects/markdown-mode/")) + +(defcustom markdown-command (let ((command (cl-loop for cmd in '("markdown" "pandoc" "markdown_py") + when (executable-find cmd) + return (file-name-nondirectory it)))) + (or command "markdown")) + "Command to run markdown." + :group 'markdown + :type '(choice (string :tag "Shell command") (repeat (string)) function)) + +(defcustom markdown-command-needs-filename nil + "Set to non-nil if `markdown-command' does not accept input from stdin. +Instead, it will be passed a filename as the final command line +option. As a result, you will only be able to run Markdown from +buffers which are visiting a file." + :group 'markdown + :type 'boolean) + +(defcustom markdown-open-command nil + "Command used for opening Markdown files directly. +For example, a standalone Markdown previewer. This command will +be called with a single argument: the filename of the current +buffer. It can also be a function, which will be called without +arguments." + :group 'markdown + :type '(choice file function (const :tag "None" nil))) + +(defcustom markdown-open-image-command nil + "Command used for opening image files directly. +This is used at `markdown-follow-link-at-point'." + :group 'markdown + :type '(choice file function (const :tag "None" nil))) + +(defcustom markdown-hr-strings + '("-------------------------------------------------------------------------------" + "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *" + "---------------------------------------" + "* * * * * * * * * * * * * * * * * * * *" + "---------" + "* * * * *") + "Strings to use when inserting horizontal rules. +The first string in the list will be the default when inserting a +horizontal rule. Strings should be listed in decreasing order of +prominence (as in headings from level one to six) for use with +promotion and demotion functions." + :group 'markdown + :type '(repeat string)) + +(defcustom markdown-bold-underscore nil + "Use two underscores when inserting bold text instead of two asterisks." + :group 'markdown + :type 'boolean) + +(defcustom markdown-italic-underscore nil + "Use underscores when inserting italic text instead of asterisks." + :group 'markdown + :type 'boolean) + +(defcustom markdown-marginalize-headers nil + "When non-nil, put opening atx header markup in a left margin. + +This setting goes well with `markdown-asymmetric-header'. But +sadly it conflicts with `linum-mode' since they both use the +same margin." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-marginalize-headers-margin-width 6 + "Character width of margin used for marginalized headers. +The default value is based on there being six heading levels +defined by Markdown and HTML. Increasing this produces extra +whitespace on the left. Decreasing it may be preferred when +fewer than six nested heading levels are used." + :group 'markdown + :type 'natnump + :safe 'natnump + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-asymmetric-header nil + "Determines if atx header style will be asymmetric. +Set to a non-nil value to use asymmetric header styling, placing +header markup only at the beginning of the line. By default, +balanced markup will be inserted at the beginning and end of the +line around the header title." + :group 'markdown + :type 'boolean) + +(defcustom markdown-indent-function 'markdown-indent-line + "Function to use to indent." + :group 'markdown + :type 'function) + +(defcustom markdown-indent-on-enter t + "Determines indentation behavior when pressing \\[newline]. +Possible settings are nil, t, and `indent-and-new-item'. + +When non-nil, pressing \\[newline] will call `newline-and-indent' +to indent the following line according to the context using +`markdown-indent-function'. In this case, note that +\\[electric-newline-and-maybe-indent] can still be used to insert +a newline without indentation. + +When set to `indent-and-new-item' and the point is in a list item +when \\[newline] is pressed, the list will be continued on the next +line, where a new item will be inserted. + +When set to nil, simply call `newline' as usual. In this case, +you can still indent lines using \\[markdown-cycle] and continue +lists with \\[markdown-insert-list-item]. + +Note that this assumes the variable `electric-indent-mode' is +non-nil (enabled). When it is *disabled*, the behavior of +\\[newline] and `\\[electric-newline-and-maybe-indent]' are +reversed." + :group 'markdown + :type '(choice (const :tag "Don't automatically indent" nil) + (const :tag "Automatically indent" t) + (const :tag "Automatically indent and insert new list items" indent-and-new-item))) + +(defcustom markdown-enable-wiki-links nil + "Syntax highlighting for wiki links. +Set this to a non-nil value to turn on wiki link support by default. +Support can be toggled later using the `markdown-toggle-wiki-links' +function or \\[markdown-toggle-wiki-links]." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.2")) + +(defcustom markdown-wiki-link-alias-first t + "When non-nil, treat aliased wiki links like [[alias text|PageName]]. +Otherwise, they will be treated as [[PageName|alias text]]." + :group 'markdown + :type 'boolean + :safe 'booleanp) + +(defcustom markdown-wiki-link-search-subdirectories nil + "When non-nil, search for wiki link targets in subdirectories. +This is the default search behavior for GitHub and is +automatically set to t in `gfm-mode'." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.2")) + +(defcustom markdown-wiki-link-search-parent-directories nil + "When non-nil, search for wiki link targets in parent directories. +This is the default search behavior of Ikiwiki." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.2")) + +(defcustom markdown-wiki-link-search-type nil + "Searching type for markdown wiki link. + +sub-directories: search for wiki link targets in sub directories +parent-directories: search for wiki link targets in parent directories +project: search for wiki link targets under project root" + :group 'markdown + :type '(set + (const :tag "search wiki link from subdirectories" sub-directories) + (const :tag "search wiki link from parent directories" parent-directories) + (const :tag "search wiki link under project root" project)) + :package-version '(markdown-mode . "2.5")) + +(make-obsolete-variable 'markdown-wiki-link-search-subdirectories 'markdown-wiki-link-search-type "2.5") +(make-obsolete-variable 'markdown-wiki-link-search-parent-directories 'markdown-wiki-link-search-type "2.5") + +(defcustom markdown-wiki-link-fontify-missing nil + "When non-nil, change wiki link face according to existence of target files. +This is expensive because it requires checking for the file each time the buffer +changes or the user switches windows. It is disabled by default because it may +cause lag when typing on slower machines." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.2")) + +(defcustom markdown-uri-types + '("acap" "cid" "data" "dav" "fax" "file" "ftp" + "gopher" "http" "https" "imap" "ldap" "mailto" + "mid" "message" "modem" "news" "nfs" "nntp" + "pop" "prospero" "rtsp" "service" "sip" "tel" + "telnet" "tip" "urn" "vemmi" "wais") + "Link types for syntax highlighting of URIs." + :group 'markdown + :type '(repeat (string :tag "URI scheme"))) + +(defcustom markdown-url-compose-char + '(?鈭 ?鈥 ?鈰 ?# ?鈽 ?鈿) + "Placeholder character for hidden URLs. +This may be a single character or a list of characters. In case +of a list, the first one that satisfies `char-displayable-p' will +be used." + :type '(choice + (character :tag "Single URL replacement character") + (repeat :tag "List of possible URL replacement characters" + character)) + :package-version '(markdown-mode . "2.3")) + +(defcustom markdown-blockquote-display-char + '("鈻" "鈹" ">") + "String to display when hiding blockquote markup. +This may be a single string or a list of string. In case of a +list, the first one that satisfies `char-displayable-p' will be +used." + :type 'string + :type '(choice + (string :tag "Single blockquote display string") + (repeat :tag "List of possible blockquote display strings" string)) + :package-version '(markdown-mode . "2.3")) + +(defcustom markdown-hr-display-char + '(?鈹 ?鈹 ?-) + "Character for hiding horizontal rule markup. +This may be a single character or a list of characters. In case +of a list, the first one that satisfies `char-displayable-p' will +be used." + :group 'markdown + :type '(choice + (character :tag "Single HR display character") + (repeat :tag "List of possible HR display characters" character)) + :package-version '(markdown-mode . "2.3")) + +(defcustom markdown-definition-display-char + '(?鈦 ?鈦 ?鈮 ?鈱 ?鈼 ?:) + "Character for replacing definition list markup. +This may be a single character or a list of characters. In case +of a list, the first one that satisfies `char-displayable-p' will +be used." + :type '(choice + (character :tag "Single definition list character") + (repeat :tag "List of possible definition list characters" character)) + :package-version '(markdown-mode . "2.3")) + +(defcustom markdown-enable-math nil + "Syntax highlighting for inline LaTeX and itex expressions. +Set this to a non-nil value to turn on math support by default. +Math support can be enabled, disabled, or toggled later using +`markdown-toggle-math' or \\[markdown-toggle-math]." + :group 'markdown + :type 'boolean + :safe 'booleanp) +(make-variable-buffer-local 'markdown-enable-math) + +(defcustom markdown-enable-html t + "Enable font-lock support for HTML tags and attributes." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-enable-highlighting-syntax nil + "Enable highlighting syntax." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.5")) + +(defcustom markdown-css-paths nil + "List of URLs of CSS files to link to in the output XHTML." + :group 'markdown + :type '(repeat (string :tag "CSS File Path"))) + +(defcustom markdown-content-type "text/html" + "Content type string for the http-equiv header in XHTML output. +When set to an empty string, this attribute is omitted. Defaults to +`text/html'." + :group 'markdown + :type 'string) + +(defcustom markdown-coding-system nil + "Character set string for the http-equiv header in XHTML output. +Defaults to `buffer-file-coding-system' (and falling back to +`utf-8' when not available). Common settings are `iso-8859-1' +and `iso-latin-1'. Use `list-coding-systems' for more choices." + :group 'markdown + :type 'coding-system) + +(defcustom markdown-export-kill-buffer t + "Kill output buffer after HTML export. +When non-nil, kill the HTML output buffer after +exporting with `markdown-export'." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-xhtml-header-content "" + "Additional content to include in the XHTML block." + :group 'markdown + :type 'string) + +(defcustom markdown-xhtml-body-preamble "" + "Content to include in the XHTML block, before the output." + :group 'markdown + :type 'string + :safe 'stringp + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-xhtml-body-epilogue "" + "Content to include in the XHTML block, after the output." + :group 'markdown + :type 'string + :safe 'stringp + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-xhtml-standalone-regexp + "^\\(<\\?xml\\| Links & Images menu." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.3")) +(make-variable-buffer-local 'markdown-hide-urls) + +(defcustom markdown-translate-filename-function #'identity + "Function to use to translate filenames when following links. +\\\\[markdown-follow-thing-at-point] and \\[markdown-follow-link-at-point] +call this function with the filename as only argument whenever +they encounter a filename (instead of a URL) to be visited and +use its return value instead of the filename in the link. For +example, if absolute filenames are actually relative to a server +root directory, you can set +`markdown-translate-filename-function' to a function that +prepends the root directory to the given filename." + :group 'markdown + :type 'function + :risky t + :package-version '(markdown-mode . "2.4")) + +(defcustom markdown-max-image-size nil + "Maximum width and height for displayed inline images. +This variable may be nil or a cons cell (MAX-WIDTH . MAX-HEIGHT). +When nil, use the actual size. Otherwise, use ImageMagick to +resize larger images to be of the given maximum dimensions. This +requires Emacs to be built with ImageMagick support." + :group 'markdown + :package-version '(markdown-mode . "2.4") + :type '(choice + (const :tag "Use actual image width" nil) + (cons (choice (sexp :tag "Maximum width in pixels") + (const :tag "No maximum width" nil)) + (choice (sexp :tag "Maximum height in pixels") + (const :tag "No maximum height" nil))))) + +(defcustom markdown-mouse-follow-link t + "Non-nil means mouse on a link will follow the link. +This variable must be set before loading markdown-mode." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.5")) + +(defcustom markdown-table-align-p t + "Non-nil means that table is aligned after table operation." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.5")) + +(defcustom markdown-fontify-whole-heading-line nil + "Non-nil means fontify the whole line for headings. +This is useful when setting a background color for the +markdown-header-face-* faces." + :group 'markdown + :type 'boolean + :safe 'booleanp + :package-version '(markdown-mode . "2.5")) + + +;;; Markdown-Specific `rx' Macro ============================================== + +;; Based on python-rx from python.el. +(eval-and-compile + (defconst markdown-rx-constituents + `((newline . ,(rx "\n")) + ;; Note: #405 not consider markdown-list-indent-width however this is never used + (indent . ,(rx (or (repeat 4 " ") "\t"))) + (block-end . ,(rx (and (or (one-or-more (zero-or-more blank) "\n") line-end)))) + (numeral . ,(rx (and (one-or-more (any "0-9#")) "."))) + (bullet . ,(rx (any "*+:-"))) + (list-marker . ,(rx (or (and (one-or-more (any "0-9#")) ".") + (any "*+:-")))) + (checkbox . ,(rx "[" (any " xX") "]"))) + "Markdown-specific sexps for `markdown-rx'") + + (defun markdown-rx-to-string (form &optional no-group) + "Markdown mode specialized `rx-to-string' function. +This variant supports named Markdown expressions in FORM. +NO-GROUP non-nil means don't put shy groups around the result." + (let ((rx-constituents (append markdown-rx-constituents rx-constituents))) + (rx-to-string form no-group))) + + (defmacro markdown-rx (&rest regexps) + "Markdown mode specialized rx macro. +This variant of `rx' supports common Markdown named REGEXPS." + (cond ((null regexps) + (error "No regexp")) + ((cdr regexps) + (markdown-rx-to-string `(and ,@regexps) t)) + (t + (markdown-rx-to-string (car regexps) t))))) + + +;;; Regular Expressions ======================================================= + +(defconst markdown-regex-comment-start + "") + (setq-local comment-start-skip "