Skip to content

Commit 6b3bd31

Browse files
committed
Implement electric characters
This defines various keys that will trigger auto reindentation when appropriate. This approach is similar to how `cc-mode' implements electric keys. Hooks are added to ensure that, unless customized otherwise, `haskell-indentation-electric-flag' will be updated along with the value of `electric-indent-mode' in the buffer. Closes #1133.
1 parent 01eb3fc commit 6b3bd31

File tree

1 file changed

+71
-17
lines changed

1 file changed

+71
-17
lines changed

haskell-indentation.el

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,23 @@
7474
:type 'integer
7575
:group 'haskell-indentation)
7676

77-
(defconst haskell-indentation-mode-map
77+
(defcustom haskell-indentation-electric-flag nil
78+
"Non-nil means insertion of some characters may auto reindent the line.
79+
This value is set to the value of the variable `electric-indent-mode'
80+
unless it is set to 'always."
81+
:type 'symbol
82+
:group 'haskell-indentation)
83+
(make-variable-buffer-local 'haskell-indentation-electric-flag)
84+
85+
(defvar haskell-indentation-mode-map
7886
(let ((map (make-sparse-keymap)))
79-
(define-key map (kbd "RET") 'haskell-indentation-newline-and-indent)
80-
(define-key map (kbd "<backtab>") 'haskell-indentation-indent-backwards)
87+
(define-key map (kbd "RET") #'haskell-indentation-newline-and-indent)
88+
(define-key map (kbd "<backtab>") #'haskell-indentation-indent-backwards)
89+
(define-key map (kbd ",") #'haskell-indentation-common-electric-command)
90+
(define-key map (kbd ";") #'haskell-indentation-common-electric-command)
91+
(define-key map (kbd ")") #'haskell-indentation-common-electric-command)
92+
(define-key map (kbd "}") #'haskell-indentation-common-electric-command)
93+
(define-key map (kbd "]") #'haskell-indentation-common-electric-command)
8194
map)
8295
"Keymap for `haskell-indentation-mode'.")
8396

@@ -87,18 +100,25 @@
87100
It rebinds RET, DEL and BACKSPACE, so that indentations can be
88101
set and deleted as if they were real tabs."
89102
:keymap haskell-indentation-mode-map
90-
(kill-local-variable 'indent-line-function)
91-
(kill-local-variable 'indent-region-function)
92103

93104
(when haskell-indentation-mode
94105
(when (and (bound-and-true-p haskell-indent-mode)
95106
(fboundp 'turn-off-haskell-indent))
96107
(turn-off-haskell-indent))
97-
(when (and (bound-and-true-p haskell-simple-indent-mode)
98-
(fboundp 'haskell-simple-indent-mode))
99-
(haskell-simple-indent-mode 0))
100-
(setq-local indent-line-function 'haskell-indentation-indent-line)
101-
(setq-local indent-region-function 'haskell-indentation-indent-region)))
108+
(setq-local indent-line-function #'haskell-indentation-indent-line)
109+
(setq-local indent-region-function #'haskell-indentation-indent-region)
110+
111+
;; 24.3 doesn't have this mode or the hooks
112+
(when (fboundp 'electric-indent-local-mode)
113+
(haskell-indentation-electric-hook)
114+
(add-hook 'electric-indent-mode-hook
115+
#'haskell-indentation-electric-hook
116+
nil
117+
t)
118+
(add-hook 'electric-indent-local-mode-hook
119+
#'haskell-indentation-electric-local-hook
120+
nil
121+
t))))
102122

103123
;;;###autoload
104124
(defun turn-on-haskell-indentation ()
@@ -135,7 +155,7 @@ set and deleted as if they were real tabs."
135155
Called from a program, takes three arguments, START, END and ARG.
136156
You can remove all indentation from a region by giving a large
137157
negative ARG. Handles bird style literate Haskell too."
138-
(interactive "r\np")
158+
(interactive "*r\np")
139159
(save-excursion
140160
(goto-char end)
141161
(let ((end-marker (point-marker)))
@@ -171,7 +191,7 @@ negative ARG. Handles bird style literate Haskell too."
171191

172192
(defun haskell-indentation-newline-and-indent ()
173193
"Insert newline and indent."
174-
(interactive)
194+
(interactive "*")
175195
;; On RET (or C-j), we:
176196
;; - just jump to the next line if literate haskell, but outside code
177197
(if (haskell-indentation-bird-outside-code-p)
@@ -224,7 +244,7 @@ Do nothing inside multiline comments and multiline strings.
224244
Start enumerating the indentation points to the right. The user
225245
can continue by repeatedly pressing TAB. When there is no more
226246
indentation points to the right, we switch going to the left."
227-
(interactive)
247+
(interactive "*")
228248
;; try to repeat
229249
(when (not (haskell-indentation-indent-line-repeat))
230250
(setq haskell-indentation-dyn-last-direction nil)
@@ -296,7 +316,7 @@ fixes up only indentation."
296316

297317
(defun haskell-indentation-indent-backwards ()
298318
"Indent the current line to the previous indentation point."
299-
(interactive)
319+
(interactive "*")
300320
(cond
301321
((and (memq last-command
302322
'(indent-for-tab-command haskell-indentation-indent-backwards))
@@ -323,7 +343,42 @@ fixes up only indentation."
323343
(car (haskell-indentation-first-indentation)) cursor-in-whitespace)
324344
(haskell-indentation-reindent-to pi cursor-in-whitespace))))))
325345

326-
346+
(defun haskell-indentation-common-electric-command (arg)
347+
"Call `self-insert-command' to insert the character typed ARG times
348+
and indent when all of the following are true:
349+
350+
1) The character is the first non-whitespace character on the line.
351+
2) There is only one possible indentation position.
352+
3) The variable `haskell-use-electric-keys' is non-nil.
353+
4) The point is not in a comment, string, or quasiquote."
354+
(interactive "*p")
355+
(let* ((col (haskell-indentation-current-indentation))
356+
(at-indent? (= col (current-column)))
357+
ind)
358+
(self-insert-command arg)
359+
(when (and at-indent?
360+
haskell-indentation-electric-flag
361+
(> arg 0)
362+
(not (nth 8 (syntax-ppss)))
363+
(= 1 (save-excursion
364+
(move-to-column col)
365+
(length (setq ind (haskell-indentation-find-indentations))))))
366+
(haskell-indentation-reindent-to (car ind)))))
367+
368+
(defun haskell-indentation-electric-hook ()
369+
"Update `haskell-indentation-electric-flag' in all Haskell buffers."
370+
(mapc (lambda (buf)
371+
(with-current-buffer buf
372+
(when (eq major-mode 'haskell-mode)
373+
(haskell-indentation-electric-local-hook))))
374+
(buffer-list)))
375+
376+
(defun haskell-indentation-electric-local-hook ()
377+
"Update `haskell-indentation-electric-flag' unless its value is 'always."
378+
(unless (eq haskell-indentation-electric-flag 'always)
379+
(setq haskell-indentation-electric-flag electric-indent-mode)))
380+
381+
327382
;;----------------------------------------------------------------------------
328383
;; Parser Starts Here
329384

@@ -740,8 +795,7 @@ For example
740795
(throw 'parse-end nil))))))
741796

742797
(defun haskell-indentation-toplevel-where ()
743-
"Parse 'where' that we may hit as a standalone in module
744-
declaration."
798+
"Parse 'where' that we may hit as a standalone in module declaration."
745799
(haskell-indentation-read-next-token)
746800

747801
(when (eq current-token 'end-tokens)

0 commit comments

Comments
 (0)