From 6cf86ed4c1c7b5a7b74630347de0dfb413d3cd18 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Thu, 4 Nov 2021 21:44:46 +0100 Subject: Add new basic Emacs Lisp code formatting function * lisp/emacs-lisp/pp.el (pp-emacs-lisp-code): New interface function. (pp): Mention it. (pp--insert-lisp, pp--format-vector, pp--format-list) (pp--format-function, pp--format-definition, pp--insert-binding) (pp--insert, pp--indent-buffer): New helper functions. --- .../lisp/emacs-lisp/pp-resources/code-formats.erts | 97 ++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/lisp/emacs-lisp/pp-resources/code-formats.erts (limited to 'test/lisp/emacs-lisp/pp-resources/code-formats.erts') diff --git a/test/lisp/emacs-lisp/pp-resources/code-formats.erts b/test/lisp/emacs-lisp/pp-resources/code-formats.erts new file mode 100644 index 00000000000..821e326867b --- /dev/null +++ b/test/lisp/emacs-lisp/pp-resources/code-formats.erts @@ -0,0 +1,97 @@ +Code: + (lambda () + (emacs-lisp-mode) + (let ((code (read (current-buffer)))) + (erase-buffer) + (pp-emacs-lisp-code code) + (untabify (point-min) (point-max)))) + +Name: code-formats1 + +=-= +(defun foo (bar) + "Yes." + (let ((a 1) + (b 2)) + (zot 1 2 (funcall bar 2)))) +=-=-= + + +Name: code-formats2 + +=-= +(defun pp-emacs-lisp-code (sexp) + "Insert SEXP into the current buffer, formatted as Emacs Lisp code." + (require 'edebug) + (let ((start (point)) + (standard-output (current-buffer))) + (pp--insert-lisp sexp) + (insert "\n") + (goto-char start) + (indent-sexp))) +=-=-= + + +Name: code-formats3 + +=-= +(defun foo (bar) + "Yes." + (let ((a 1) + (b 2)) + (zot-zot-zot-zot-zot-zot 1 2 (funcall + bar-bar-bar-bar-bar-bar-bar-bar-bar-bar 2)))) +=-=-= + + +Name: code-formats4 + +=-= +(defun foo (bar) + "Yes." + (let ((a 1) + (b 2) + foo bar zotfoo bar zotfoo bar zotfoo bar zotfoo bar zotfoo bar zotfoo + bar zot) + (zot 1 2 (funcall bar 2)))) +=-=-= + + +Name: code-formats5 + +=-= +(defgroup pp () + "Pretty printer for Emacs Lisp." + :prefix "pp-" + :group 'lisp) +=-=-= + +Name: code-formats6 + +=-= +(defcustom pp-escape-newlines t + "Value of `print-escape-newlines' used by pp-* functions." + :type 'boolean + :group 'pp) +=-=-= + +Name: code-formats7 + +=-= +(defun pp (object &optional stream) + (princ (pp-to-string object) (or stream standard-output))) +=-=-= + + +Name: code-formats8 + +=-= +(defun pp-eval-expression (expression) + "Evaluate EXPRESSION and pretty-print its value. +Also add the value to the front of the list in the variable `values'." + (interactive (list (read--expression "Eval: "))) + (message "Evaluating...") + (let ((result (eval expression lexical-binding))) + (values--store-value result) + (pp-display-expression result "*Pp Eval Output*"))) +=-=-= -- cgit v1.2.3 From fb1267d90a5c25a29dbdae170ea065e017b05d29 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Thu, 4 Nov 2021 22:07:48 +0100 Subject: Indent lambdas/closures better * lisp/emacs-lisp/pp.el (pp--format-function): Indent lambdas and closures better. --- lisp/emacs-lisp/pp.el | 13 ++++++++++++- test/lisp/emacs-lisp/pp-resources/code-formats.erts | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'test/lisp/emacs-lisp/pp-resources/code-formats.erts') diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el index ca3f2a270e2..1d7b935b6d9 100644 --- a/lisp/emacs-lisp/pp.el +++ b/lisp/emacs-lisp/pp.el @@ -236,9 +236,20 @@ Ignores leading comment characters." (defun pp--format-function (sexp) (let* ((sym (car sexp)) (edebug (get sym 'edebug-form-spec)) - (indent (get sym 'lisp-indent-function))) + (indent (get sym 'lisp-indent-function)) + (doc (get sym 'doc-string-elt))) (when (eq indent 'defun) (setq indent 2)) + ;; We probably want to keep all the elements before the doc string + ;; on a single line. + (when doc + (setq indent (1- doc))) + ;; Special-case closures -- these shouldn't really exist in actual + ;; source code, so there's no indentation information. But make + ;; them output slightly better. + (when (and (not indent) + (eq sym 'closure)) + (setq indent 0)) (pp--insert "(" sym) (pop sexp) ;; Get the first entries on the first line. diff --git a/test/lisp/emacs-lisp/pp-resources/code-formats.erts b/test/lisp/emacs-lisp/pp-resources/code-formats.erts index 821e326867b..f48e262f69d 100644 --- a/test/lisp/emacs-lisp/pp-resources/code-formats.erts +++ b/test/lisp/emacs-lisp/pp-resources/code-formats.erts @@ -95,3 +95,11 @@ Also add the value to the front of the list in the variable `values'." (values--store-value result) (pp-display-expression result "*Pp Eval Output*"))) =-=-= + +Name: code-formats9 + +=-= +(lambda () + (interactive) + 1) +=-=-= -- cgit v1.2.3 From 4cf7af5a2a9aecb067d95b96a5965cc74627e86b Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Thu, 4 Nov 2021 23:16:47 +0100 Subject: Tweak multi-line expressions in pp--format-function * lisp/emacs-lisp/pp.el (pp--format-function): Fix up multi-line expressions. --- lisp/emacs-lisp/pp.el | 10 ++++++++-- test/lisp/emacs-lisp/pp-resources/code-formats.erts | 13 ++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'test/lisp/emacs-lisp/pp-resources/code-formats.erts') diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el index 67a21c53915..df837fa691e 100644 --- a/lisp/emacs-lisp/pp.el +++ b/lisp/emacs-lisp/pp.el @@ -267,8 +267,14 @@ Ignores leading comment characters." ;; Get the first entries on the first line. (if indent (pp--format-definition sexp indent edebug) - (while sexp - (pp--insert " " (pop sexp)))) + (let ((prev 0)) + (while sexp + (let ((start (point))) + ;; Don't put sexps on the same line as a multi-line sexp + ;; preceding it. + (pp--insert (if (> prev 1) "\n" " ") + (pop sexp)) + (setq prev (count-lines start (point))))))) (insert ")"))) (defun pp--format-definition (sexp indent edebug) diff --git a/test/lisp/emacs-lisp/pp-resources/code-formats.erts b/test/lisp/emacs-lisp/pp-resources/code-formats.erts index f48e262f69d..c2733d84dae 100644 --- a/test/lisp/emacs-lisp/pp-resources/code-formats.erts +++ b/test/lisp/emacs-lisp/pp-resources/code-formats.erts @@ -40,7 +40,8 @@ Name: code-formats3 (let ((a 1) (b 2)) (zot-zot-zot-zot-zot-zot 1 2 (funcall - bar-bar-bar-bar-bar-bar-bar-bar-bar-bar 2)))) + bar-bar-bar-bar-bar-bar-bar-bar-bar-bar + 2)))) =-=-= @@ -103,3 +104,13 @@ Name: code-formats9 (interactive) 1) =-=-= + + +Name: code-formats10 + +=-= +(funcall foo (concat "zot" (if (length> site 0) site + "bar") + "+" + (string-replace " " "+" query))) +=-=-= -- cgit v1.2.3 From c1865384d736cf290971e049a9d2f7869c7396da Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Fri, 5 Nov 2021 15:27:03 +0100 Subject: Allow 'pp' to limit the line widths * lisp/emacs-lisp/pp.el (pp-max-width, pp-use-max-width): New user options (bug#11934). (pp-to-string): Use it. (pp--insert-lisp): Tweak whether to use standard-output or not. (pp--max-width): New function. --- etc/NEWS | 5 ++ lisp/emacs-lisp/pp.el | 90 ++++++++++++++++------ .../lisp/emacs-lisp/pp-resources/code-formats.erts | 8 ++ 3 files changed, 78 insertions(+), 25 deletions(-) (limited to 'test/lisp/emacs-lisp/pp-resources/code-formats.erts') diff --git a/etc/NEWS b/etc/NEWS index 08ff1f1e697..9b4112a8f2b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -422,6 +422,11 @@ Use 'exif-parse-file' and 'exif-field' instead. * Lisp Changes in Emacs 29.1 +--- +*** New user option 'pp-use-max-width'. +If non-nil, 'pp' will attempt to limit the line length when formatting +long lists and vectors. + --- *** New function 'pp-emacs-lisp-code'. 'pp' formats general Lisp sexps. This function does much the same, diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el index ffaa2aeb93a..8ef5979a270 100644 --- a/lisp/emacs-lisp/pp.el +++ b/lisp/emacs-lisp/pp.el @@ -33,22 +33,43 @@ (defcustom pp-escape-newlines t "Value of `print-escape-newlines' used by pp-* functions." + :type 'boolean) + +(defcustom pp-max-width t + "Max width to use when formatting. +If nil, there's no max width. If t, use the window width. +Otherwise this should be a number." + :type '(choice (const :tag "none" nil) + (const :tag "window width" t) + number) + :version "29.1") + +(defcustom pp-use-max-width nil + "If non-nil, `pp'-related functions will try to fold lines. +The target width is given by the `pp-max-width' variable." :type 'boolean - :group 'pp) + :version "29.1") + +(defvar pp--inhibit-function-formatting nil) ;;;###autoload (defun pp-to-string (object) "Return a string containing the pretty-printed representation of OBJECT. OBJECT can be any Lisp object. Quoting characters are used as needed to make output that `read' can handle, whenever this is possible." - (with-temp-buffer - (lisp-mode-variables nil) - (set-syntax-table emacs-lisp-mode-syntax-table) - (let ((print-escape-newlines pp-escape-newlines) - (print-quoted t)) - (prin1 object (current-buffer))) - (pp-buffer) - (buffer-string))) + (if pp-use-max-width + (let ((pp--inhibit-function-formatting t)) + (with-temp-buffer + (pp-emacs-lisp-code object) + (buffer-string))) + (with-temp-buffer + (lisp-mode-variables nil) + (set-syntax-table emacs-lisp-mode-syntax-table) + (let ((print-escape-newlines pp-escape-newlines) + (print-quoted t)) + (prin1 object (current-buffer))) + (pp-buffer) + (buffer-string)))) ;;;###autoload (defun pp-buffer () @@ -56,7 +77,6 @@ to make output that `read' can handle, whenever this is possible." (interactive) (goto-char (point-min)) (while (not (eobp)) - ;; (message "%06d" (- (point-max) (point))) (cond ((ignore-errors (down-list 1) t) (save-excursion @@ -86,6 +106,9 @@ can handle, whenever this is possible. This function does not apply special formatting rules for Emacs Lisp code. See `pp-emacs-lisp-code' instead. +By default, this function won't limit the line length of lists +and vectors. Bind `pp-use-max-width' to a non-nil value to do so. + Output stream is STREAM, or value of `standard-output' (which see)." (princ (pp-to-string object) (or stream standard-output))) @@ -191,17 +214,19 @@ Ignores leading comment characters." ;;;###autoload (defun pp-emacs-lisp-code (sexp) - "Insert SEXP into the current buffer, formatted as Emacs Lisp code." + "Insert SEXP into the current buffer, formatted as Emacs Lisp code. +Use the `pp-max-width' variable to control the desired line length." (require 'edebug) - (let ((standard-output (current-buffer))) - (save-restriction - (narrow-to-region (point) (point)) + (let ((obuf (current-buffer))) + (with-temp-buffer + (emacs-lisp-mode) (pp--insert-lisp sexp) (insert "\n") (goto-char (point-min)) (indent-sexp) (while (re-search-forward " +$" nil t) - (replace-match ""))))) + (replace-match "")) + (insert-into-buffer obuf)))) (defun pp--insert-lisp (sexp) (cl-case (type-of sexp) @@ -213,30 +238,35 @@ Ignores leading comment characters." (cond ((symbolp (cadr sexp)) (let ((print-quoted t)) - (prin1 sexp))) + (prin1 sexp (current-buffer)))) ((consp (cadr sexp)) (insert "'") (pp--format-list (cadr sexp) (set-marker (make-marker) (1- (point)))))) (pp--format-list sexp))) (t - (princ sexp)))) + (princ sexp (current-buffer))))) ;; Print some of the smaller integers as characters, perhaps? (integer (if (<= ?0 sexp ?z) (let ((print-integers-as-characters t)) - (princ sexp)) - (princ sexp))) + (princ sexp (current-buffer))) + (princ sexp (current-buffer)))) (string (let ((print-escape-newlines t)) - (prin1 sexp))) - (otherwise (princ sexp)))) + (prin1 sexp (current-buffer)))) + (otherwise (princ sexp (current-buffer))))) (defun pp--format-vector (sexp) - (prin1 sexp)) + (insert "[") + (cl-loop for i from 0 + for element across sexp + do (pp--insert (and (> i 0) " ") element)) + (insert "]")) (defun pp--format-list (sexp &optional start) (if (and (symbolp (car sexp)) + (not pp--inhibit-function-formatting) (not (keywordp (car sexp)))) (pp--format-function sexp) (insert "(") @@ -292,7 +322,7 @@ Ignores leading comment characters." (cl-decf indent)) (when (stringp (car sexp)) (insert "\n") - (prin1 (pop sexp))) + (prin1 (pop sexp) (current-buffer))) ;; Then insert the rest with line breaks before each form. (while sexp (insert "\n") @@ -329,7 +359,7 @@ Ignores leading comment characters." (pp--insert-lisp thing)) ;; We need to indent what we have so far to see if we have to fold. (pp--indent-buffer) - (when (> (current-column) (window-width)) + (when (> (current-column) (pp--max-width)) (save-excursion (goto-char start) (unless (looking-at "[ \t]+$") @@ -338,13 +368,23 @@ Ignores leading comment characters." (goto-char (point-max)) ;; If we're still too wide, then go up one step and try to ;; insert a newline there. - (when (> (current-column) (window-width)) + (when (> (current-column) (pp--max-width)) (condition-case () (backward-up-list 1) (:success (when (looking-back " " 2) (insert "\n"))) (error nil))))))) +(defun pp--max-width () + (cond ((numberp pp-max-width) + pp-max-width) + ((null pp-max-width) + most-positive-fixnum) + ((eq pp-max-width t) + (window-width)) + (t + (error "Invalid pp-max-width value: %s" pp-max-width)))) + (defun pp--indent-buffer () (goto-char (point-min)) (while (not (eobp)) diff --git a/test/lisp/emacs-lisp/pp-resources/code-formats.erts b/test/lisp/emacs-lisp/pp-resources/code-formats.erts index c2733d84dae..2b2001d0964 100644 --- a/test/lisp/emacs-lisp/pp-resources/code-formats.erts +++ b/test/lisp/emacs-lisp/pp-resources/code-formats.erts @@ -114,3 +114,11 @@ Name: code-formats10 "+" (string-replace " " "+" query))) =-=-= + + +Name: code-formats11 + +=-= +(lambda () + [(foo bar) (foo bar)]) +=-=-= -- cgit v1.2.3