Skip to content

Implement electric characters #1302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/haskell-mode.texi
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ position, @kbd{S-TAB} selects the previous one. If a block is selected
you can use @kbd{TAB} to indent the block more and @kbd{S-TAB} to indent
the block less.

When @code{electric-indent-mode} is enabled or the variable
@code{haskell-indentation-electric-flag} is non-nil, the insertion of
some characters (by default @kbd{,} @kbd{;} @kbd{)} @kbd{@}} @kbd{]})
may trigger auto reindentation under appropriate conditions. See the
documentation of @code{haskell-indentation-common-electric-command} for
more details.

@item @code{haskell-indent-mode} (optional).

This is a semi-intelligent indentation mode doing a decent job at
Expand Down
79 changes: 55 additions & 24 deletions haskell-indentation.el
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,23 @@
:type 'integer
:group 'haskell-indentation)

(defconst haskell-indentation-mode-map
(defcustom haskell-indentation-electric-flag nil
"Non-nil means insertion of some characters may auto reindent the line.
If the variable `electric-indent-mode' is non-nil then this variable is
overridden."
:type 'symbol
:group 'haskell-indentation)
(make-variable-buffer-local 'haskell-indentation-electric-flag)

(defvar haskell-indentation-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") 'haskell-indentation-newline-and-indent)
(define-key map (kbd "<backtab>") 'haskell-indentation-indent-backwards)
(define-key map (kbd "RET") #'haskell-indentation-newline-and-indent)
(define-key map (kbd "<backtab>") #'haskell-indentation-indent-backwards)
(define-key map (kbd ",") #'haskell-indentation-common-electric-command)
(define-key map (kbd ";") #'haskell-indentation-common-electric-command)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kill-local-variables should stay here. Reason: minor modes may be switched on and off, this code path is for switching off (although it is executed always).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I was thinking that someone would always want either haskell-indent or haskell-indentation. I guess someone might want neither.

(define-key map (kbd ")") #'haskell-indentation-common-electric-command)
(define-key map (kbd "}") #'haskell-indentation-common-electric-command)
(define-key map (kbd "]") #'haskell-indentation-common-electric-command)
map)
"Keymap for `haskell-indentation-mode'.")

Expand All @@ -94,11 +107,8 @@ set and deleted as if they were real tabs."
(when (and (bound-and-true-p haskell-indent-mode)
(fboundp 'turn-off-haskell-indent))
(turn-off-haskell-indent))
(when (and (bound-and-true-p haskell-simple-indent-mode)
(fboundp 'haskell-simple-indent-mode))
(haskell-simple-indent-mode 0))
(setq-local indent-line-function 'haskell-indentation-indent-line)
(setq-local indent-region-function 'haskell-indentation-indent-region)))
(setq-local indent-line-function #'haskell-indentation-indent-line)
(setq-local indent-region-function #'haskell-indentation-indent-region)))

;;;###autoload
(defun turn-on-haskell-indentation ()
Expand Down Expand Up @@ -135,7 +145,7 @@ set and deleted as if they were real tabs."
Called from a program, takes three arguments, START, END and ARG.
You can remove all indentation from a region by giving a large
negative ARG. Handles bird style literate Haskell too."
(interactive "r\np")
(interactive "*r\np")
(save-excursion
(goto-char end)
(let ((end-marker (point-marker)))
Expand Down Expand Up @@ -171,7 +181,7 @@ negative ARG. Handles bird style literate Haskell too."

(defun haskell-indentation-newline-and-indent ()
"Insert newline and indent."
(interactive)
(interactive "*")
;; On RET (or C-j), we:
;; - just jump to the next line if literate haskell, but outside code
(if (haskell-indentation-bird-outside-code-p)
Expand Down Expand Up @@ -224,7 +234,7 @@ Do nothing inside multiline comments and multiline strings.
Start enumerating the indentation points to the right. The user
can continue by repeatedly pressing TAB. When there is no more
indentation points to the right, we switch going to the left."
(interactive)
(interactive "*")
;; try to repeat
(when (not (haskell-indentation-indent-line-repeat))
(setq haskell-indentation-dyn-last-direction nil)
Expand Down Expand Up @@ -296,7 +306,7 @@ fixes up only indentation."

(defun haskell-indentation-indent-backwards ()
"Indent the current line to the previous indentation point."
(interactive)
(interactive "*")
(cond
((and (memq last-command
'(indent-for-tab-command haskell-indentation-indent-backwards))
Expand All @@ -323,7 +333,30 @@ fixes up only indentation."
(car (haskell-indentation-first-indentation)) cursor-in-whitespace)
(haskell-indentation-reindent-to pi cursor-in-whitespace))))))


(defun haskell-indentation-common-electric-command (arg)
"Call `self-insert-command' to insert the character typed ARG times
and indent when all of the following are true:
1) The character is the first non-whitespace character on the line.
2) There is only one possible indentation position.
3) The variable `electric-indent-mode' or `haskell-indentation-electric-flag'
is non-nil.
4) The point is not in a comment, string, or quasiquote."
(interactive "*p")
(let ((col (current-column))
ind)
(self-insert-command arg)
(when (and (or haskell-indentation-electric-flag
electric-indent-mode)
(= (haskell-indentation-current-indentation)
col)
(> arg 0)
(not (nth 8 (syntax-ppss)))
(= 1 (save-excursion
(move-to-column col)
(length (setq ind (haskell-indentation-find-indentations))))))
(haskell-indentation-reindent-to (car ind)))))


;;----------------------------------------------------------------------------
;; Parser Starts Here

Expand Down Expand Up @@ -679,16 +712,15 @@ For example
(haskell-indentation-with-starter
#'haskell-indentation-type-1)))))
((string= current-token "where")
(let ((starter-indent-inside (current-column)))
(haskell-indentation-with-starter
#'haskell-indentation-expression-layout nil)
(cond
((equal current-token 'end-tokens)
(when (string= following-token "deriving")
(haskell-indentation-add-left-indent)))
((equal current-token "deriving")
(haskell-indentation-with-starter
#'haskell-indentation-expression-layout nil)
(cond
((equal current-token 'end-tokens)
(when (string= following-token "deriving")
(haskell-indentation-add-left-indent)))
((equal current-token "deriving")
(haskell-indentation-with-starter
#'haskell-indentation-type-1)))))))
#'haskell-indentation-type-1))))))

(defun haskell-indentation-import ()
"Parse import declaration."
Expand Down Expand Up @@ -741,8 +773,7 @@ For example
(throw 'parse-end nil))))))

(defun haskell-indentation-toplevel-where ()
"Parse 'where' that we may hit as a standalone in module
declaration."
"Parse 'where' that we may hit as a standalone in module declaration."
(haskell-indentation-read-next-token)

(when (eq current-token 'end-tokens)
Expand Down