Download
(require 'newcomment)
(eval-when-compile
(require 'cl))
(defvar elisp-format-buildin-keywords-regexp
(let (keywords-alist keywords-list keywords-regexp)
(with-temp-buffer
(emacs-lisp-mode)
(setq font-lock-mode t)
(font-lock-fontify-buffer)
(setq keywords-alist (cadr font-lock-keywords)))
(dolist (element keywords-alist)
(setq element
(if (car-safe element)
(car element)
element))
(when (stringp element)
(setq element (replace-regexp-in-string "^(" "" element))
(push element keywords-list)))
(dolist (element keywords-list)
(setq keywords-regexp (concat keywords-regexp
(cond ((eq element (first keywords-list))
(format "\\(%s\\|" element))
((eq element (car (last
keywords-list)))
(format "%s\\)" element))
(t (format "%s\\|" element))))))
keywords-regexp)
"The regular expression that match `build-in' keywords.")
(defvar elisp-format-define-keyword-regexp nil
"The regexp expression for `elisp-format-define-keyword-list'.")
(defvar elisp-format-newline-keyword-addons-regexp nil
"The regular expression for `elisp-format-newline-keyword-addons-list'.")
(defvar elisp-format-split-subexp-keyword-addons-regexp nil
"The regular expression for `elisp-format-split-subexp-keyword-addons-list'.")
(defvar elisp-format-debug-mode nil
"Display details debug information when this option is `non-nil'.")
(defgroup elisp-format nil
"Format elisp code."
:group 'tools)
(defcustom elisp-format-batch-program "emacs"
"The program name for execute batch command."
:type 'string
:group 'elisp-format)
(defcustom elisp-format-column 100
"The column number to truncate long line."
:type 'integer
:group 'elisp-format)
(defcustom elisp-format-indent-comment t
"Whether indent comment.
If `non-nil', will indent comment.
Default it t."
:type 'boolean
:group 'elisp-format)
(defcustom elisp-format-dired-mark-files-confirm t
"Whether confirmation is needed to format dired mark files.
If `non-nil' will notify you before format dired mark files.
Default is t."
:type 'boolean
:group 'elisp-format)
(defcustom elisp-format-define-keyword-list
'(
"defun" "defun*" "defsubst" "defmacro" "defadvice" "define-skeleton"
"define-minor-mode" "define-global-minor-mode"
"define-globalized-minor-mode" "define-derived-mode" "define-generic-mode"
"define-compiler-macro" "define-modify-macro" "defsetf"
"define-setf-expander" "define-method-combination" "defgeneric" "defmethod"
"defalias"
"defvar" "defconst" "defconstant" "defcustom" "defparameter"
"define-symbol-macro"
"defgroup" "deftheme" "deftype" "defstruct" "defclass" "define-condition"
"define-widget" "defface" "defpackage")
"This list contain define-keywords for format.
Copy those value from `lisp-imenu-generic-expression' define.
Or have a exist variable contain those define-keywords?"
:type 'list
:set (lambda (symbol value)
(set symbol value)
(setq elisp-format-define-keyword-regexp (regexp-opt value)))
:group 'elisp-format)
(defcustom elisp-format-newline-keyword-addons-list
'("interactive" "setq" "set" "buffer-substring"
"buffer-substring-no-properties")
"This list contain addons keywords for newline.
The line beginning match keywords in this list will be newline."
:type 'list
:set (lambda (symbol value)
(set symbol value)
(setq elisp-format-newline-keyword-addons-regexp (regexp-opt value)))
:group 'elisp-format)
(defcustom elisp-format-newline-keyword-except-list '()
"The list contain except keywords for newline.
The line beginning match keywords in this list won't be newline."
:type 'list
:group 'elisp-format)
(defcustom elisp-format-split-subexp-keyword-addons-list
'("and" "or" "buffer-substring" "buffer-substring-no-properties"
"font-lock-add-keywords")
"The list contain addons keywords that will split it's sub-expression."
:type 'list
:set (lambda (symbol value)
(set symbol value)
(setq elisp-format-split-subexp-keyword-addons-regexp (regexp-opt
value)))
:group 'elisp-format)
(defcustom elisp-format-split-subexp-keyword-except-list
'("provide" "require" "loop" "throw" "featurep")
"The list contain except keywords that won't split it's sub-expression."
:type 'list
:group 'elisp-format)
(defcustom elisp-format-split-subexp-keyword-keep-alist
'((1 . ("and" "or" "let" "let*" "while" "when" "catch" "unless" "if" "dolist"
"dotimes" "lambda" "cond" "condition-case" "with-current-buffer"
"with-temp-message" "with-selected-window"
"with-output-to-temp-buffer" "with-selected-frame"))
(2 . (
"defun" "defun*" "defsubst" "defmacro" "defadvice" "define-skeleton"
"define-minor-mode" "define-global-minor-mode"
"define-globalized-minor-mode" "define-derived-mode"
"define-generic-mode" "define-compiler-macro" "define-modify-macro"
"defsetf" "define-setf-expander" "define-method-combination"
"defgeneric" "defmethod" "defalias"
"defvar" "defconst" "defconstant" "defcustom" "defparameter"
"define-symbol-macro"
"defgroup" "deftheme" "deftype" "defstruct" "defclass"
"define-condition" "define-widget" "defface" "defpackage" ))
(5 . ("loop")))
"The list contain keywords that will be split it's sub-expression."
:type 'alist
:group 'elisp-format)
(defun elisp-format-region (&optional start end)
"Format current region or buffer.
This function will format region from START to END.
Or try to format `defun' around point."
(interactive)
(let ((start-time (elisp-format-get-current-time)))
(message "Format %s ..." (buffer-name))
(check-parens)
(save-excursion
(unless (and start
end)
(if mark-active
(progn
(setq start (region-beginning))
(setq end (region-end))
(deactivate-mark))
(setq start
(progn
(beginning-of-defun)
(point)))
(setq end
(progn
(end-of-defun)
(point)))))
(setq end (copy-marker end))
(elisp-format-delete-whitespace start end)
(when elisp-format-debug-mode
(message "`elisp-format-delete-whitespace' module completed."))
(elisp-format-split-same-level-expression start end)
(when elisp-format-debug-mode
(message "`elisp-format-split-same-level-expression' module completed."))
(elisp-format-split-list-data-type start end)
(when elisp-format-debug-mode
(message "`elisp-format-split-list-data-type' module completed."))
(elisp-format-split-keyword start end)
(when elisp-format-debug-mode
(message "`elisp-format-split-keyword' module completed."))
(elisp-format-split-define-assoc-value start end)
(when elisp-format-debug-mode
(message "`elisp-format-split-define-assoc-value' module completed."))
(elisp-format-split-subexp start end)
(when elisp-format-debug-mode
(message "`elisp-format-split-subexp' module completed."))
(goto-char start)
(while (< (point) end)
(unless (and (bolp)
(eolp))
(elisp-format-split-and-indent))
(forward-line +1))
(when elisp-format-debug-mode
(message "`elisp-format-split-and-indent' module completed."))
(elisp-format-join-close-parentheses)
(when elisp-format-debug-mode
(message "`elisp-format-join-close-parentheses' module completed."))
(if elisp-format-indent-comment
(elisp-format-indent-comment-region start end))
(when elisp-format-debug-mode
(message "`elisp-format-indent-comment-region' module completed."))
(message "Format %s completed (%ss)." (buffer-name)
(/ (- (elisp-format-get-current-time) start-time) 1000000)))))
(defun elisp-format-buffer ()
"Format current buffer."
(interactive)
(elisp-format-region (point-min)
(point-max)))
(defun elisp-format-file (filename)
"Format file with FILENAME."
(interactive "fFile name: ")
(with-current-buffer (find-file-noselect filename)
(elisp-format-buffer)))
(defun elisp-format-file-batch (filename &optional surpress-popup-window)
"Format elisp FILENAME.
But instead in `batch-mode'.
If SURPRESS-POPUP-WINDOW is non-nil, don't show output window."
(interactive "fFile name: ")
(elisp-format-batch-command "elisp-format-file-batch"
elisp-format-batch-program
(format
"-batch -l %s --eval=\"(progn (require 'elisp-format) (elisp-format-file \\\"%s\\\"))\""
(find-library-name "elisp-format") filename)
surpress-popup-window))
(defun elisp-format-directory (dir)
"Format recursive elisp files under DIR."
(interactive "DDirectory: ")
(let ((suffix (format "^.*\\.el%s$" (regexp-opt load-file-rep-suffixes))))
(dolist (file (directory-files dir t))
(if (file-directory-p file)
(unless (string-match "^\\.\\.?$" (file-name-nondirectory file))
(elisp-format-directory file))
(unless (string-match "^\\.?#" (file-name-nondirectory file))
(if (string-match suffix (file-name-nondirectory file))
(elisp-format-file file)))))))
(defun elisp-format-directory-batch (dir &optional surpress-popup-window)
"Format recursive elisp files under DIR.
But instead in `batch-mode'.
If SURPRESS-POPUP-WINDOW is non-nil, don't show output window."
(interactive "DDirectory: ")
(elisp-format-batch-command "elisp-format-directory-batch"
elisp-format-batch-program
(format
"-batch -l %s --eval=\"(progn (require 'elisp-format) (elisp-format-directory \\\"%s\\\"))\""
(find-library-name "elisp-format") dir)
surpress-popup-window))
(defun elisp-format-dired-mark-files ()
"Format dired mark files."
(interactive)
(if (or (not elisp-format-dired-mark-files-confirm)
(yes-or-no-p "Do you want format marked files? "))
(dolist (filename (dired-get-marked-files))
(elisp-format-file filename))))
(defun elisp-format-library (library)
"Format LIBRARY."
(interactive (list
(let* ((dirs load-path)
(suffixes (find-library-suffixes)))
(completing-read "Library name: " (apply-partially
'locate-file-completion-table
dirs suffixes)))))
(elisp-format-file (find-library-name library)))
(defun elisp-format-split-keyword-internal (subexp)
"Split keyword.
SUBEXP is sub-expression number for regexp match."
(let (match-beg-position match-length match-keyword)
(setq match-beg-position (match-beginning subexp))
(setq match-length (length (match-string subexp)))
(goto-char match-beg-position)
(cond
((or
(elisp-format-in-comment-p)
(elisp-format-beginning-of-comment-line-p))
(forward-line +1))
((elisp-format-in-string-p)
(goto-char (elisp-format-string-end-position)))
(t
(setq match-keyword (elisp-format-get-match-keyword))
(unless (member match-keyword elisp-format-newline-keyword-except-list)
(unless (or (elisp-format-first-non-blank-of-line-p)
(elisp-format-prev-sexp-list-data-type-p))
(newline))
(forward-list)
(unless (or (elisp-format-last-sexp-of-line-p))
(newline))
)
(goto-char match-beg-position)
(forward-char match-length)))))
(defun elisp-format-split-subexp-internal (subexp)
"Split sub-expression that match keyword.
SUBEXP is sub-expression number for regexp match."
(let (match-beg-position match-length match-keyword subsexp-start subsexp-end
subsexp-keep (subsexp-counter 0))
(setq match-beg-position (match-beginning subexp))
(setq match-length (length (match-string subexp)))
(goto-char match-beg-position)
(cond
((or
(elisp-format-in-comment-p)
(elisp-format-beginning-of-comment-line-p))
(forward-line +1))
((elisp-format-in-string-p)
(goto-char (elisp-format-string-end-position)))
(t
(setq subsexp-start match-beg-position)
(forward-list)
(setq subsexp-end (copy-marker (point)))
(goto-char subsexp-start)
(setq match-keyword (elisp-format-get-match-keyword))
(unless (member match-keyword
elisp-format-split-subexp-keyword-except-list)
(goto-char subsexp-start)
(search-forward-regexp " \\|$" nil t)
(catch 'reach-last-sexp
(while (< (point) subsexp-end)
(skip-chars-forward " \t\n")
(while (string-equal (string (char-after)) ")
(move-end-of-line 1)
(skip-chars-forward " \t\n"))
(incf subsexp-counter)
(setq subsexp-keep (elisp-format-is-keep-subexp match-keyword
subsexp-counter))
(unless (or subsexp-keep
(elisp-format-first-non-blank-of-line-p)
(elisp-format-prev-sexp-list-data-type-p))
(newline))
(unless
(ignore-errors
(forward-sexp)
(unless (or subsexp-keep
(elisp-format-last-sexp-of-line-p)
(elisp-format-prev-sexp-list-data-type-p))
(newline))
t)
(throw 'reach-last-sexp "Can't find next sexp")))))
(goto-char subsexp-start)
(search-forward-regexp " \\|$" nil t)))))
(defun elisp-format-split-and-indent ()
"Split and indent lines."
(funcall indent-line-function)
(let ((original-line (elisp-format-line-number)))
(when elisp-format-debug-mode
(message "%s" original-line))
(call-interactively 'move-end-of-line)
(skip-chars-backward " \t")
(when (> (current-column) elisp-format-column)
(move-to-column elisp-format-column t)
(cond
((elisp-format-in-comment-p)
(goto-line original-line))
((elisp-format-in-string-p)
(elisp-format-split-string)
(when elisp-format-debug-mode
(message "Format string at %s completed." original-line)))
(t
(elisp-format-split-code original-line)
(when elisp-format-debug-mode
(message "Format code at %s completed." original-line)))))))
(defun elisp-format-split-string ()
"Split current string with appropriate column.
Default this function will split current line to make
end column less than value of `elisp-format-column'.
And this action just advice, it don't split deep
if current line can't split any more."
(let (string-length indent-length)
(goto-char (elisp-format-string-beg-position))
(unless (or (elisp-format-first-non-blank-of-line-p)
(elisp-format-first-subsexp-of-sexp-p))
(newline-and-indent))
(setq indent-length (- (point)
(line-beginning-position)))
(forward-char +1)
(setq string-length (elisp-format-string-length))
(goto-char (elisp-format-string-end-position))
(unless (elisp-format-last-non-blank-of-line-p)
(if (> (+ indent-length string-length) elisp-format-column)
(unless (elisp-format-last-sexp-of-line-p)
(newline-and-indent)
(forward-line -1))
(forward-line -1)))))
(defun elisp-format-split-code (original-line)
"Split current code with appropriate column.
Default this function will split current line to make
end column less than value of `elisp-format-column'.
And this action just advice, it don't split deep
if current line can't split any more.
Argument ORIGINAL-LINE is position before parse."
(skip-chars-forward " \t")
(search-backward-regexp " \\|^" nil t)
(skip-chars-forward " \t")
(unless (or (elisp-format-define-keyword-assoc-name-p)
(elisp-format-first-non-blank-of-line-p))
(newline-and-indent))
(search-forward-regexp " \\|$" nil t)
(skip-chars-backward " \t")
(unless (elisp-format-last-non-blank-of-line-p)
(if (> (current-column) elisp-format-column)
(progn
(newline-and-indent)
(forward-line -1))
(goto-line original-line))))
(defun elisp-format-line-number (&optional pos)
"Return (narrowed) buffer line number at position POS.
If POS is nil, use current buffer location."
(let ((original-position (or pos
(point))) start)
(save-excursion
(goto-char (point-min))
(setq start (point))
(goto-char original-position)
(forward-line 0)
(1+ (count-lines start (point))))))
(defun elisp-format-delete-whitespace (&optional start end)
"Delete un-necessary whitespace between START and END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(delete-trailing-whitespace)
(let (search-start search-end)
(goto-char start)
(while (re-search-forward "\n\\s-+" end t)
(setq search-start (match-beginning 0))
(setq search-end (match-end 0))
(unless (elisp-format-in-string-p)
(if (and
(progn
(goto-char search-start)
(not (elisp-format-in-comment-p)))
(progn
(goto-char search-end)
(forward-char +1)
(not (elisp-format-in-comment-p))))
(progn
(goto-char search-end)
(join-line))
(goto-char search-end)))))
(goto-char start)
(while (re-search-forward "(\\s-+" end t)
(unless (and (elisp-format-in-comment-p)
(elisp-format-in-string-p))
(kill-region (1+ (match-beginning 0))
(match-end 0)))))
(defun elisp-format-split-subexp (&optional start end)
"Split sub-expression format START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(let ((search-regexp (concat "\\s-*\\([`',]?@?(+" "\\("
elisp-format-buildin-keywords-regexp "\\|"
elisp-format-split-subexp-keyword-addons-regexp
"\\)" "\\)\\b[^\\B\\s_-]")))
(while (re-search-forward search-regexp end t)
(elisp-format-split-subexp-internal 1))))
(defun elisp-format-split-define-assoc-value (&optional start end)
"Split association value of keyword format START to END.
Keyword is match in `elisp-format-define-keyword-list'."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(let ((search-regexp (concat "\\s-*\\([`',]?@?(+"
elisp-format-define-keyword-regexp
"\\)\\b[^\\B\\s_-]")) search-start value-start
value-length)
(while (re-search-forward search-regexp end t)
(setq search-start (match-end 1))
(unless (and (elisp-format-in-comment-p)
(elisp-format-in-string-p))
(unless
(ignore-errors
(forward-sexp)
(skip-chars-forward " \t\n")
(setq value-start (point))
(forward-sexp)
(setq value-length (- (point) value-start))
(goto-char value-start)
(if (and (not (elisp-format-first-non-blank-of-line-p))
(> (+ (current-column) value-length) elisp-format-column))
(newline)))
(goto-char search-start))))))
(defun elisp-format-split-same-level-expression (&optional start end)
"Split same level expression from START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(let (search-end)
(while (re-search-forward "\\()\\|\\]\\)\\(\\s-*\\)[`',]?@?\\((\\|\\[\\)"
end t)
(setq search-end (match-end 2))
(unless (or (elisp-format-in-comment-p)
(elisp-format-in-string-p))
(goto-char search-end)
(newline)))))
(defun elisp-format-split-list-data-type (&optional start end)
"Split list type data (:keyword) from START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(let (search-beg search-end)
(while (re-search-forward "'*\\B:+[^: \n]+\\b" end t)
(setq search-beg (match-beginning 0))
(setq search-end (match-end 0))
(unless (or (elisp-format-in-comment-p)
(elisp-format-in-string-p))
(goto-char search-beg)
(save-excursion
(unless (or (elisp-format-first-non-blank-of-line-p)
(elisp-format-first-subsexp-of-sexp-p))
(ignore-errors
(skip-chars-backward " \t")
(backward-sexp)
(unless (elisp-format-first-subsexp-of-sexp-p)
(goto-char search-beg)
(newline)))
(goto-char search-beg)
(forward-sexp)
(skip-chars-forward " \t\n")
(when (looking-at "'*:")
(goto-char search-beg)
(newline))))
(forward-sexp)
(skip-chars-forward " \t")
(unless (looking-at "'*\\(:\\|)\\)")
(ignore-errors
(forward-sexp)
(unless (elisp-format-last-sexp-of-line-p)
(forward-sexp)
(unless (elisp-format-last-sexp-of-line-p)
(backward-sexp)
(newline)))))
(goto-char search-end)))))
(defun elisp-format-split-keyword (&optional start end)
"Split keyword from START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(let ((search-regexp (concat "\\s-*\\([`',]?@?(+" "\\("
elisp-format-buildin-keywords-regexp "\\|"
elisp-format-newline-keyword-addons-regexp "\\)"
"\\)\\b[^\\B\\s_-]")))
(while (re-search-forward search-regexp end t)
(elisp-format-split-keyword-internal 1))))
(defun elisp-format-join-close-parentheses (&optional start end)
"Join standalone close parentheses from START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(let (search-start search-end)
(goto-char start)
(while (re-search-forward "\\(\\s-*\n\\s-*\\))" end t)
(setq search-start (match-beginning 0))
(setq search-end (match-end 0))
(unless (elisp-format-in-string-p)
(if
(progn
(goto-char search-start)
(not (elisp-format-in-comment-p)))
(progn
(goto-char search-end)
(backward-char 1)
(join-line))
(goto-char search-end))))))
(defun elisp-format-indent-comment-region (&optional start end)
"Indent comment format START to END."
(or start
(setq start (point-min)))
(or end
(setq end (point-max)))
(goto-char start)
(while (< (point) end)
(if (comment-search-forward end t)
(comment-indent)
(goto-char end))))
(defun elisp-format-get-match-keyword ()
"Get match keyword after point."
(save-excursion
(search-forward "(")
(symbol-name (symbol-at-point))))
(defun elisp-format-define-keyword-assoc-name-p ()
"Return t if current string around point is association keyword name.
And keyword in match `elisp-format-define-keyword-list'."
(let (define-keyword-name)
(if (looking-back "^\\s-*(\\([^() ]+\\)\\s-+")
(progn
(setq define-keyword-name (match-string 1))
(member define-keyword-name elisp-format-define-keyword-list))
nil)))
(defun elisp-format-define-keyword-assoc-value-p ()
"Return t if current string around point is association keyword value.
And keyword in match `elisp-format-define-keyword-list'."
(save-excursion
(let ((original-position (point)))
(if
(beginning-of-defun)
(progn
(search-forward-regexp " \\|$" nil t)
(forward-sexp 1)
(skip-chars-forward " \t\n")
(if (and (>= original-position (point))
(<= original-position
(progn
(forward-list)
(point))))
t
nil))
nil))))
(defun elisp-format-first-non-blank-of-line-p ()
"Return t if point is first non-blank character of line.
Otherwise return nil."
(let ((current-point (point)))
(save-excursion
(back-to-indentation)
(equal current-point (point)))))
(defun elisp-format-last-non-blank-of-line-p ()
"Return t if point is last non-blank character of line.
Otherwise return nil."
(let ((current-point (point)))
(save-excursion
(call-interactively 'move-end-of-line)
(skip-chars-backward " \t")
(equal current-point (point)))))
(defun elisp-format-first-subsexp-of-sexp-p ()
"Return t if point is first sub-sexp of sexp.
Otherwise return nil."
(looking-back "([ \t\n]*"))
(defun elisp-format-last-sexp-of-line-p ()
"Return t if point is last sexp of line.
Otherwise return nil."
(looking-at "\\([ \t\n)]*$\\|\\s-*))
(defun elisp-format-prev-sexp-list-data-type-p ()
"Return t if previous sexp is keyword beginning with ':'.
Otherwise return nil."
(save-excursion
(ignore-errors
(backward-sexp)
(string-match "^:[^ ]+$"
(buffer-substring-no-properties
(point)
(progn
(search-forward-regexp " \\|$" nil t)
(skip-chars-backward " \t")
(point)))))))
(defun elisp-format-is-keep-subexp (keyword index)
"Return `non-nil' if KEYWORD is keep keyword.
This function will search KEYWORD with INDEX
in `elisp-format-split-subexp-keyword-keep-list'
return `non-nil', when search match keyword.
Otherwise, return nil."
(catch 'match
(loop for (keep-index . keep-keyword-list) in
elisp-format-split-subexp-keyword-keep-alist do
(when (and (<= index keep-index)
(member keyword keep-keyword-list))
(throw 'match t)))
nil))
(defun elisp-format-current-parse-state ()
"Return parse state of point from beginning of defun."
(let ((point (point)))
(beginning-of-defun)
(parse-partial-sexp (point) point)))
(defun elisp-format-in-string-p (&optional state)
"True if the parse STATE is within a double-quote-delimited string.
If no parse state is supplied, compute one from the beginning of the
defun to the point."
(and (nth 3 (or state
(elisp-format-current-parse-state)))
t))
(defun elisp-format-in-comment-p (&optional state)
"True if parse state STATE is within a comment.
If no parse state is supplied, compute one from the beginning of the
defun to the point."
(and (nth 4 (or state
(elisp-format-current-parse-state)))
t))
(defun elisp-format-string-start+end-points (&optional state)
"Return a cons of the points of open and close quotes of the string.
The string is determined from the parse state STATE, or the parse state
from the beginning of the defun to the point.
This assumes that `elisp-format-in-string-p' has already returned true, i.e.
that the point is already within a string."
(save-excursion
(let ((start (nth 8 (or state
(elisp-format-current-parse-state)))))
(goto-char start)
(forward-sexp 1)
(cons start (1- (point))))))
(defun elisp-format-beginning-of-comment-line-p ()
"Return t if current point is beginning of comment line.
And current line only have comment.
Otherwise return nil."
(and (bolp)
(looking-at "\\s-*)))
(defun elisp-format-string-beg-position ()
"Return the beginning position of string."
(car (elisp-format-string-start+end-points)))
(defun elisp-format-string-end-position ()
"Return the end position of string."
(1+ (cdr (elisp-format-string-start+end-points))))
(defun elisp-format-string-length ()
"Return string length."
(- (elisp-format-string-end-position)
(elisp-format-string-beg-position)))
(defun elisp-format-get-current-time ()
"Get current time (microsecond)."
(let ((time (current-time)))
(+ (* 1000000 (string-to-number (concat (number-to-string (nth 0 time))
(number-to-string (nth 1 time)))))
(nth 2 time))))
(defun elisp-format-batch-command (name command command-args &optional surpress-popup-window)
"Run special `batch' command.
NAME is sub-process buffer title.
COMMAND is command running under `batch'.
COMMAND-ARGS is command arguments for COMMAND.
If SURPRESS-POPUP-WINDOW is non-nil, don't show output window."
(let* ((time-now (current-time))
(output-buffer (format "*%s<%s-%s-%s>" name (nth 0 time-now)
(nth 1 time-now)
(nth 2 time-now))))
(start-process-shell-command name output-buffer command command-args)
(unless surpress-popup-window
(pop-to-buffer output-buffer))))
(provide 'elisp-format)