diff --git a/haskell-cabal.el b/haskell-cabal.el index f93d535fe..4f76c33b5 100644 --- a/haskell-cabal.el +++ b/haskell-cabal.el @@ -569,13 +569,28 @@ resulting buffer-content" (haskell-cabal-add-indentation (- ,old-l1-indent ,new-l1-indent)))))))) +(defun haskell-cabal-comma-separatorp (pos) + "Return non-nil when the char at POS is a comma separator. +Characters that are not a comma, or commas inside a commment or +string, are not comma separators." + (when (eq (char-after pos) ?,) + (let ((ss (syntax-ppss pos))) + (not + (or + ;; inside a string + (nth 3 ss) + ;; inside a comment + (nth 4 ss)))))) + + (defun haskell-cabal-strip-list () - "strip commas from comma-seperated list" + "Strip commas from a comma-separated list." (goto-char (point-min)) -;; split list items on single line + ;; split list items on single line (while (re-search-forward - "\\([^ \t,\n]\\)[ \t]*,[ \t]*\\([^ \t,\n]\\)" nil t) - (replace-match "\\1\n\\2" nil nil)) + "\\([^ \t,\n]\\)[ \t]*\\(,\\)[ \t]*\\([^ \t,\n]\\)" nil t) + (when (haskell-cabal-comma-separatorp (match-beginning 2)) + (replace-match "\\1\n\\3" nil nil))) (goto-char (point-min)) (while (re-search-forward "^\\([ \t]*\\),\\([ \t]*\\)" nil t) (replace-match "" nil nil)) @@ -586,7 +601,7 @@ resulting buffer-content" (haskell-cabal-each-line (haskell-cabal-chomp-line))) (defun haskell-cabal-listify () - "Add commas so that buffer contains a comma-seperated list" + "Add commas so that the buffer contains a comma-seperated list" (cl-case haskell-cabal-list-comma-position ('before (goto-char (point-min)) @@ -605,14 +620,36 @@ resulting buffer-content" (insert ",") (beginning-of-line)))))) +(defun haskell-cabal-comma-separatedp () + "Return non-nil when the current buffer contains a comma-separated list. +When the buffer contains at least one comma separator (checked +with `haskell-cabal-comma-separatorp'), the buffer is considered +to be a comma-separated list." + (let ((comma-separatedp nil)) + (goto-char (point-min)) + (while (and (not comma-separatedp) + (search-forward "," (point-max) t)) + (when (haskell-cabal-comma-separatorp (match-beginning 0)) + (setq comma-separatedp t)) + ;; Make sure we don't find the same comma every time + (forward-char 1)) + comma-separatedp)) (defmacro haskell-cabal-with-cs-list (&rest funs) - "format buffer so that each line contains a list element " - `(progn - (save-excursion (haskell-cabal-strip-list)) - (unwind-protect (progn ,@funs) - (haskell-cabal-listify)))) + "Format the buffer so that each line contains a list element. +Keep the lines comma-separated if and only if they were in the +first place." + (let ((comma-separatedp (make-symbol "comma-separatedp"))) + `(let ((,comma-separatedp + (save-excursion + (prog1 + (haskell-cabal-comma-separatedp) + (haskell-cabal-strip-list))))) + (unwind-protect (progn ,@funs) + ;; Only reinsert commas when it already was comma-separated. + (when ,comma-separatedp + (haskell-cabal-listify)))))) (defun haskell-cabal-sort-lines-key-fun () diff --git a/tests/haskell-cabal-tests.el b/tests/haskell-cabal-tests.el index 41f5eda25..3f52eb858 100644 --- a/tests/haskell-cabal-tests.el +++ b/tests/haskell-cabal-tests.el @@ -50,6 +50,124 @@ (haskell-cabal-previous-subsection) (haskell-cabal-previous-section)))) +(ert-deftest haskell-cabal-subsection-arrange-lines-keep-commas () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + Build-Depends: base, + bytestring, + directory, + filepath, + text + Ghc-Options: -O -Wall +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "Build-Depends:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + Build-Depends: base + , bytestring + , directory + , filepath + , text + Ghc-Options: -O -Wall +")))) + +(ert-deftest haskell-cabal-subsection-arrange-lines-no-commas () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + Other-Modules: Some.Module + Some.Other.Other.Module + Some.Other.Module +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "Other-Modules:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + Other-Modules: Some.Module + Some.Other.Module + Some.Other.Other.Module +")))) + + +(ert-deftest haskell-cabal-subsection-arrange-lines-quoted-items () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -fprof-auto \"-with-rtsopts=-N -p -s -h -i0.1\" +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "GHC-Options:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -fprof-auto \"-with-rtsopts=-N -p -s -h -i0.1\" +")))) + +(ert-deftest haskell-cabal-subsection-arrange-lines-no-commas-quoted-comma () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -Wall -fprof-auto \"foo, bar\" +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "GHC-Options:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -Wall -fprof-auto \"foo, bar\" +")))) + +(ert-deftest haskell-cabal-subsection-arrange-lines-commas-quoted-comma () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -Wall, -fprof-auto \"foo, bar\" +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "GHC-Options:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + GHC-Options: -Wall + , -fprof-auto \"foo, bar\" +")))) + +(ert-deftest haskell-cabal-subsection-arrange-lines-comma-in-commment () + (should (with-temp-buffer + (insert "Executable bin-1 + Main-Is: TestParsing.hs + Other-Modules: Some.Module + Some.Other.Other.Module + Some.Other.Module + -- Foo, bar +") + (haskell-cabal-mode) + (goto-char (point-min)) + (search-forward "Other-Modules:") + (haskell-cabal-subsection-arrange-lines) + (string= (buffer-string) + "Executable bin-1 + Main-Is: TestParsing.hs + Other-Modules: Some.Module + Some.Other.Module + Some.Other.Other.Module + -- Foo, bar +")))) + (provide 'haskell-cabal-tests) ;;; haskell-cabal-tests.el ends here