From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: npostavs@users.sourceforge.net Newsgroups: gmane.emacs.bugs Subject: bug#21343: 24.5; parse-partial-sexp mistakes string for a comment Date: Sun, 05 Mar 2017 08:59:23 -0500 Message-ID: <87a88zakp0.fsf@users.sourceforge.net> References: <87lhczjyx5.fsf@gmail.com> <55DCA9C7.7070609@easy-emacs.de> <87pp2awaqp.fsf@gmail.com> <87lh1jfnpj.fsf@users.sourceforge.net> <87d1dw9rqa.fsf@users.sourceforge.net> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1488722356 28113 195.159.176.226 (5 Mar 2017 13:59:16 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sun, 5 Mar 2017 13:59:16 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) Cc: 21343@debbugs.gnu.org To: Oleh Krehel Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sun Mar 05 14:59:12 2017 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ckWgp-0006nY-7q for geb-bug-gnu-emacs@m.gmane.org; Sun, 05 Mar 2017 14:59:11 +0100 Original-Received: from localhost ([::1]:38880 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ckWgv-0000zi-DF for geb-bug-gnu-emacs@m.gmane.org; Sun, 05 Mar 2017 08:59:17 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:53739) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ckWgl-0000zd-IH for bug-gnu-emacs@gnu.org; Sun, 05 Mar 2017 08:59:09 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ckWgg-0002Jv-KW for bug-gnu-emacs@gnu.org; Sun, 05 Mar 2017 08:59:07 -0500 Original-Received: from debbugs.gnu.org ([208.118.235.43]:42419) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ckWgg-0002JF-Ex for bug-gnu-emacs@gnu.org; Sun, 05 Mar 2017 08:59:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1ckWgg-0003ZN-2W for bug-gnu-emacs@gnu.org; Sun, 05 Mar 2017 08:59:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: npostavs@users.sourceforge.net Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 05 Mar 2017 13:59:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 21343 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: confirmed patch Original-Received: via spool by 21343-submit@debbugs.gnu.org id=B21343.148872229913669 (code B ref 21343); Sun, 05 Mar 2017 13:59:01 +0000 Original-Received: (at 21343) by debbugs.gnu.org; 5 Mar 2017 13:58:19 +0000 Original-Received: from localhost ([127.0.0.1]:40618 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ckWfx-0003YO-RC for submit@debbugs.gnu.org; Sun, 05 Mar 2017 08:58:18 -0500 Original-Received: from mail-it0-f47.google.com ([209.85.214.47]:34754) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ckWfv-0003Y9-Jn for 21343@debbugs.gnu.org; Sun, 05 Mar 2017 08:58:16 -0500 Original-Received: by mail-it0-f47.google.com with SMTP id m27so2567584iti.1 for <21343@debbugs.gnu.org>; Sun, 05 Mar 2017 05:58:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=ud8CB2Rul4dAFyaTDpkdFsMQz+fV2KR+NJhqTVDjxlk=; b=A9kcHf25x5iAp6a8un0/s35qTT0STq4QNPdl05txyUDQjkI7CR/zNHbNiprbo6MpKy gEg9lWRPpJMAXpAOmBSzS5t6Oi7MYmqXdSGqyn5t2sCFNGGh8YwTGR3t+lUQZRtSSGa4 bj4ceuS1lqXPt4aTdB53QsRNAYn2BxH45yczvFktwndGzzP82VKijzEQ5Hj3I9lTCf5B 2cJr0agXcXBut0Xm8/cPKbV9erFZ+YO3VbBiIUUYsb1rnp0C7rfvSh3kKQwTPtAXxhS4 vRpinbq1jQwcqrnlsQEQrS8Wc0MnvFNITBIx8qGfNMW1vTZe4/OBwy0Wypk9egVZSqK1 Ymzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:references:date :in-reply-to:message-id:user-agent:mime-version; bh=ud8CB2Rul4dAFyaTDpkdFsMQz+fV2KR+NJhqTVDjxlk=; b=Qlt3AATVjoNrqrCxHpvB/21je+OM/Dt2DsDzvNvUE1u2m2xoFB9WMYb6Eckp25O08C XykzaefgWufu3gee/owo0EUIDcQIJHkHvLCLmZWrmiOiUmmMClaiaCjCnYtl8oQ+LXNA DiyP8Qo0Lxk4xfSF7XretO0CHjQNFdStWqm+06Ha4jAXPYxfXyBn9Bsu6jR+Qsz/FycO 1RYbnR5ZC/ip7ILZVJ79Za8cAnqOMzOUq+KaLGHpqWCkaryqH/gJX7XqZnr7m0fJuShL KL8wQUW/VOHFb9aQhr13AbPFk8jdhSmhWQkRUd8GSxjU0fmwQqfJH+wh4r1tyG3eBFjG jfsw== X-Gm-Message-State: AMke39ku4xYDcjXXHw9YTvnJYnyeN3T7PqrNuTEs28fyB7D3D5mGwJxymVbMIsSFsUtpQg== X-Received: by 10.36.155.11 with SMTP id o11mr11745591itd.11.1488722290016; Sun, 05 Mar 2017 05:58:10 -0800 (PST) Original-Received: from zony ([45.2.7.65]) by smtp.googlemail.com with ESMTPSA id h91sm7047732ioi.24.2017.03.05.05.58.08 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sun, 05 Mar 2017 05:58:08 -0800 (PST) In-Reply-To: <87d1dw9rqa.fsf@users.sourceforge.net> (npostavs@users.sourceforge.net's message of "Sun, 05 Mar 2017 01:12:45 -0500") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:130207 Archived-At: --=-=-= Content-Type: text/plain npostavs@users.sourceforge.net writes: > Here is a patch (the first one just simplifies the code). I had a mistake in the first patch, here's a corrected version. --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v2-0001-lisp-emacs-lisp-lisp-mode.el-indent-sexp-Simplify.patch Content-Description: patch >From 053902dc2ad32f96c56f2231f6075a99d9596fa9 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 5 Mar 2017 00:16:13 -0500 Subject: [PATCH v2] * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Simplify. * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp): (indent-subsexp, indent-sexp-in-string): New tests. --- lisp/emacs-lisp/lisp-mode.el | 171 ++++++++++++++------------------ test/lisp/emacs-lisp/lisp-mode-tests.el | 94 ++++++++++++++++++ 2 files changed, 169 insertions(+), 96 deletions(-) create mode 100644 test/lisp/emacs-lisp/lisp-mode-tests.el diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index d720e0bc57..507fb8aebe 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1056,103 +1056,82 @@ indent-sexp If optional arg ENDPOS is given, indent each line, stopping when ENDPOS is encountered." (interactive) - (let ((indent-stack (list nil)) - (next-depth 0) - ;; If ENDPOS is non-nil, use nil as STARTING-POINT - ;; so that calculate-lisp-indent will find the beginning of - ;; the defun we are in. - ;; If ENDPOS is nil, it is safe not to scan before point - ;; since every line we indent is more deeply nested than point is. - (starting-point (if endpos nil (point))) - (last-point (point)) - last-depth bol outer-loop-done inner-loop-done state this-indent) - (or endpos - ;; Get error now if we don't have a complete sexp after point. - (save-excursion (forward-sexp 1))) + (let* ((indent-stack (list nil)) + ;; If ENDPOS is non-nil, use beginning of defun as STARTING-POINT. + ;; If ENDPOS is nil, it is safe not to scan before point + ;; since every line we indent is more deeply nested than point is. + (starting-point (save-excursion (if endpos (beginning-of-defun)) + (point))) + (state nil) + (init-depth 0) + (next-depth 0) + (last-depth 0) + (last-syntax-point (point))) + (unless endpos + ;; Get error now if we don't have a complete sexp after point. + (save-excursion (forward-sexp 1) + ;; We need a marker because we modify the buffer + ;; text preceding endpos. + (setq endpos (point-marker)))) (save-excursion - (setq outer-loop-done nil) - (while (if endpos (< (point) endpos) - (not outer-loop-done)) - (setq last-depth next-depth - inner-loop-done nil) - ;; Parse this line so we can learn the state - ;; to indent the next line. - ;; This inner loop goes through only once - ;; unless a line ends inside a string. - (while (and (not inner-loop-done) - (not (setq outer-loop-done (eobp)))) - (setq state (parse-partial-sexp (point) (progn (end-of-line) (point)) - nil nil state)) - (setq next-depth (car state)) - ;; If the line contains a comment other than the sort - ;; that is indented like code, - ;; indent it now with indent-for-comment. - ;; Comments indented like code are right already. - ;; In any case clear the in-comment flag in the state - ;; because parse-partial-sexp never sees the newlines. - (if (car (nthcdr 4 state)) - (progn (indent-for-comment) - (end-of-line) - (setcar (nthcdr 4 state) nil))) - ;; If this line ends inside a string, - ;; go straight to next line, remaining within the inner loop, - ;; and turn off the \-flag. - (if (car (nthcdr 3 state)) - (progn - (forward-line 1) - (setcar (nthcdr 5 state) nil)) - (setq inner-loop-done t))) - (and endpos - (<= next-depth 0) - (progn - (setq indent-stack (nconc indent-stack - (make-list (- next-depth) nil)) - last-depth (- last-depth next-depth) - next-depth 0))) - (forward-line 1) - ;; Decide whether to exit. - (if endpos - ;; If we have already reached the specified end, - ;; give up and do not reindent this line. - (if (<= endpos (point)) - (setq outer-loop-done t)) - ;; If no specified end, we are done if we have finished one sexp. - (if (<= next-depth 0) - (setq outer-loop-done t))) - (unless outer-loop-done - (while (> last-depth next-depth) - (setq indent-stack (cdr indent-stack) - last-depth (1- last-depth))) - (while (< last-depth next-depth) - (setq indent-stack (cons nil indent-stack) - last-depth (1+ last-depth))) - ;; Now indent the next line according - ;; to what we learned from parsing the previous one. - (setq bol (point)) - (skip-chars-forward " \t") - ;; But not if the line is blank, or just a comment - ;; (except for double-semi comments; indent them as usual). - (if (or (eobp) (looking-at "\\s<\\|\n")) - nil - (if (and (car indent-stack) - (>= (car indent-stack) 0)) - (setq this-indent (car indent-stack)) - (let ((val (calculate-lisp-indent - (if (car indent-stack) (- (car indent-stack)) - starting-point)))) - (if (null val) - (setq this-indent val) - (if (integerp val) - (setcar indent-stack - (setq this-indent val)) - (setcar indent-stack (- (car (cdr val)))) - (setq this-indent (car val)))))) - (if (and this-indent (/= (current-column) this-indent)) - (progn (delete-region bol (point)) - (indent-to this-indent))))) - (or outer-loop-done - (setq outer-loop-done (= (point) last-point)) - (setq last-point (point))))))) + (while (< (point) endpos) + ;; Parse this line so we can learn the state to indent the + ;; next line. + (while (progn + (setq state (parse-partial-sexp + last-syntax-point (progn (end-of-line) (point)) + nil nil state)) + ;; Skip over newlines within strings. + (nth 3 state)) + (setq state (parse-partial-sexp (point) (point-max) + nil nil state 'syntax-table)) + (setq last-syntax-point (point))) + (setq next-depth (car state)) + ;; If the line contains a comment indent it now with + ;; `indent-for-comment'. + (when (nth 4 state) + (indent-for-comment) + (end-of-line)) + (setq last-syntax-point (point)) + (when (< next-depth init-depth) + (setq indent-stack (nconc indent-stack + (make-list (- init-depth next-depth) nil)) + last-depth (- last-depth next-depth) + next-depth init-depth)) + (forward-line 1) + (when (< (point) endpos) + (let ((depth-delta (- next-depth last-depth))) + (cond ((< depth-delta 0) + (setq indent-stack (nthcdr (- depth-delta) indent-stack))) + ((> depth-delta 0) + (setq indent-stack (nconc (make-list depth-delta nil) + indent-stack)))) + (setq last-depth next-depth)) + ;; Now indent the next line according + ;; to what we learned from parsing the previous one. + (let ((bol (point))) + (skip-chars-forward " \t") + ;; But not if the line is blank, or just a comment (we + ;; already called `indent-for-comment' above). + (unless (or (eolp) (eq (char-syntax (char-after)) ?<)) + (let ((this-indent (car indent-stack))) + (when (listp this-indent) + (let ((val (calculate-lisp-indent + (or (car this-indent) starting-point)))) + (setq + this-indent + (cond ((integerp val) + (setf (car indent-stack) val)) + ((consp val) ; (COLUMN CONTAINING-SEXP-START) + (setf (car indent-stack) (cdr val)) + (car val)) + ;; `calculate-lisp-indent' only returns nil + ;; when we're in a string, but this won't + ;; happen because we skip strings above. + (t (error "This shouldn't happen!")))))) + (when (/= (current-column) this-indent) + (delete-region bol (point)) + (indent-to this-indent)))))))))) (defun indent-pp-sexp (&optional arg) "Indent each line of the list starting just after point, or prettyprint it. diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el new file mode 100644 index 0000000000..2801f23df6 --- /dev/null +++ b/test/lisp/emacs-lisp/lisp-mode-tests.el @@ -0,0 +1,94 @@ +;;; lisp-mode-tests.el --- Test Lisp editing commands -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; GNU Emacs 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. + +;; GNU Emacs 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. If not, see . + +;;; Code: + +(require 'ert) +(require 'cl-lib) +(require 'lisp-mode) + +(ert-deftest indent-sexp () + "Test basics of \\[indent-sexp]." + (with-temp-buffer + (insert "\ +\(a + (prog1 + (prog1 + 1 + 2) + 2) + (1 + \"string +noindent\" (\"string2 +noindent\" 3 +4) + 2) ; comment + ;; comment + b)") + (goto-char (point-min)) + (let ((indent-tabs-mode nil) + (correct (buffer-string))) + (dolist (mode '(fundamental-mode emacs-lisp-mode)) + (funcall mode) + (indent-sexp) + ;; Don't mess up correctly indented code. + (should (string= (buffer-string) correct)) + ;; Correctly add indentation. + (save-excursion + (while (not (eobp)) + (delete-horizontal-space) + (forward-line))) + (indent-sexp) + (should (equal (buffer-string) correct)) + ;; Correctly remove indentation. + (save-excursion + (let ((n 0)) + (while (not (eobp)) + (unless (looking-at "noindent") + (insert (make-string n ?\s))) + (cl-incf n) + (forward-line)))) + (indent-sexp) + (should (equal (buffer-string) correct)))))) + +(ert-deftest indent-subsexp () + "Make sure calling `indent-sexp' inside a sexp works." + (with-temp-buffer + (insert "\ +\(d1 xx + (d2 yy + zz) + 11)") + (let ((correct (buffer-string))) + (search-backward "d2") + (up-list -1) + (indent-sexp) + (should (equal (buffer-string) correct))))) + +(ert-deftest indent-sexp-in-string () + "Make sure calling `indent-sexp' inside a string works." + ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21343. + (with-temp-buffer + (emacs-lisp-mode) + (insert "\";\"") + (let ((correct (buffer-string))) + (search-backward ";") + (indent-sexp) + (should (equal (buffer-string) correct))))) + +(provide 'lisp-mode-tests) +;;; lisp-mode-tests.el ends here -- 2.11.1 --=-=-=--