diff --git a/haskell-simple-indent.el b/haskell-simple-indent.el index 979c062a9..d2e65cd60 100644 --- a/haskell-simple-indent.el +++ b/haskell-simple-indent.el @@ -96,13 +96,19 @@ position and the current line. If there is no visible indent point beyond the current column, position given by `indent-next-tab-stop' is used instead." (interactive) - (let* ((start-column (current-column)) + (let* ((start-column (or (save-excursion + (back-to-indentation) + (if (not (eolp)) + (current-column))) + (current-column))) (invisible-from nil) ; `nil' means infinity here + (found) (indent)) (save-excursion ;; Loop stops if there no more lines above this one or when has ;; found a line starting at first column. - (while (and (or (not invisible-from) + (while (and (not found) + (or (not invisible-from) (not (zerop invisible-from))) (zerop (forward-line -1))) ;; Ignore empty lines. @@ -132,30 +138,53 @@ point beyond the current column, position given by invisible-from (current-column))) ;; Signal that solution is found. - (setq invisible-from 0)))))))) - (if indent - (let ((opoint (point-marker))) - (indent-line-to indent) - (if (> opoint (point)) - (goto-char opoint)) - (set-marker opoint nil)) - (tab-to-tab-stop)))) + (setq found t)))))))) + + + (let ((opoint (point-marker))) + ;; Indent to the calculated indent or last know invisible-from + ;; or use tab-to-tab-stop. Try hard to keep cursor in the same + ;; place or move it to the indentation if it was before it. And + ;; keep content of the line intact. + (setq indent (or indent + invisible-from + (if (fboundp 'indent-next-tab-stop) + (indent-next-tab-stop start-column)) + (let ((tabs tab-stop-list)) + (while (and tabs (>= start-column (car tabs))) + (setq tabs (cdr tabs))) + (if tabs (car tabs))) + (* (/ (+ start-column tab-width) tab-width) tab-width))) + (indent-line-to indent) + (if (> opoint (point)) + (goto-char opoint)) + (set-marker opoint nil)))) (defun haskell-simple-indent-backtab () "Indent backwards. Dual to `haskell-simple-indent'." (interactive) - (back-to-indentation) - (let ((saved-column (current-column)) + (let ((saved-column (or (save-excursion + (back-to-indentation) + (if (not (eolp)) + (current-column))) + (current-column))) (i 0) (x 0)) - (delete-region (line-beginning-position) (point)) (save-excursion - (while (< (current-column) saved-column) - (haskell-simple-indent) - (setq i (+ i 1)))) - (back-to-indentation) - (delete-region (line-beginning-position) (point)) + (back-to-indentation) + (delete-region (line-beginning-position) (point))) + (while (< (or (save-excursion + (back-to-indentation) + (if (not (eolp)) + (current-column))) + (current-column)) saved-column) + (haskell-simple-indent) + (setq i (+ i 1))) + + (save-excursion + (back-to-indentation) + (delete-region (line-beginning-position) (point))) (while (< x (- i 1)) (haskell-simple-indent) (setq x (+ x 1))))) diff --git a/tests/haskell-simple-indent-tests.el b/tests/haskell-simple-indent-tests.el index 175adaa94..1ad52f8ea 100644 --- a/tests/haskell-simple-indent-tests.el +++ b/tests/haskell-simple-indent-tests.el @@ -33,6 +33,26 @@ (setq result-backward (cons (current-column) result-backward)) (list (reverse result-forward) result-backward)))) +(defun indent-and-backtab-last-line (lines &optional prepare-buffer) + (with-temp-buffer + (let (after-indent after-backtab) + (if prepare-buffer + (funcall prepare-buffer)) + (dolist (line lines) + (insert line) + (insert "\n")) + (forward-line -1) + (skip-chars-forward "^|") + (delete-char 1) + (haskell-simple-indent) + (insert "|") + (setq after-indent (buffer-substring (line-beginning-position) (line-end-position))) + (delete-char -1) + (haskell-simple-indent-backtab) + (insert "|") + (setq after-backtab (buffer-substring (line-beginning-position) (line-end-position))) + (list after-indent after-backtab)))) + (ert-deftest find-indent-positions-1 () (should (equal '(5 7 10 19 26 32 40 48 56 64) (find-indent-positions '("main = do putStrLn \"Hello World!\""))))) @@ -115,3 +135,48 @@ "" " e f g h" ""))))) + +(ert-deftest indent-and-backtab-last-line-1 () + (should (equal '("\t|" + "|") + (indent-and-backtab-last-line '("|"))))) + +(ert-deftest indent-and-backtab-last-line-2 () + (should (equal '("\t|x" + "|x") + (indent-and-backtab-last-line '("|x"))))) + +(ert-deftest indent-and-backtab-last-line-3 () + (should (equal '("\t|x" + "|x") + (indent-and-backtab-last-line '("| x"))))) + +(ert-deftest indent-and-backtab-last-line-4 () + (should (equal '(" |x" + " |x") + (indent-and-backtab-last-line '("a b c" + "| x"))))) + +(ert-deftest indent-and-backtab-last-line-5 () + (should (equal '("\tx|y" + "x|y") + (indent-and-backtab-last-line '("x|y"))))) + +(ert-deftest indent-and-backtab-last-line-6 () + (should (equal '("\t\t\tx|y" + "x|y") + (indent-and-backtab-last-line '("\t\t\tbase" + "x|y"))))) + +(ert-deftest indent-and-backtab-last-line-7 () + (should (equal '("\txy |" + " xy |") + (indent-and-backtab-last-line '(" p x" + "\t\t\tbase" + " xy |"))))) + +(ert-deftest indent-and-backtab-last-line-8 () + (should (equal '("\t\t this_is_|long" + "\t this_is_|long") + (indent-and-backtab-last-line '(" a a a a a a a a this_is_long" + " this_is_|long")))))