Skip to content

Commit 8b09aae

Browse files
committed
Merge pull request #222 from elixir-lang/correct-indentation-inside-heredoc
correct indentation inside heredoc strings
2 parents a8dbf36 + 21e2baf commit 8b09aae

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

elixir-smie.el

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,53 @@
407407
(smie-rule-hanging-p)))
408408
(smie-rule-parent elixir-smie-indent-basic))))))
409409

410+
(defun elixir-smie--heredoc-at-current-point-p ()
411+
"Return non-nil if cursor is at a string."
412+
(save-excursion
413+
(or (and (nth 3 (save-excursion
414+
(let ((pos (point)))
415+
(parse-partial-sexp 1 pos))))
416+
(nth 8 (save-excursion
417+
(let ((pos (point)))
418+
(parse-partial-sexp 1 pos)))))
419+
(and (looking-at "\"\"\"")
420+
(match-beginning 0)))))
421+
422+
(defun elixir-smie--previous-line-empty-p ()
423+
"Return non-nil if the previous line is blank."
424+
(save-excursion
425+
(forward-line -1)
426+
(and (eolp) (bolp))))
427+
428+
(defun elixir-smie--previous-line-indentation ()
429+
"Return the indentation of the previous line."
430+
(save-excursion
431+
(forward-line -1)
432+
(current-indentation)))
433+
434+
;; Add the custom function to handle indentation inside heredoc to the
435+
;; smie-indent-functions list. The indentation function will only be
436+
;; process inside an elixir-mode.
437+
(defun elixir-smie--indent-inside-heredoc ()
438+
"Handle indentation inside Elixir heredocs.
439+
440+
Rules:
441+
1. If the previous line is empty, indent as the basic indentation
442+
at the beginning of the heredoc.
443+
2. If the previous line is not empty, indent as the previous line.
444+
"
445+
(if (eq major-mode 'elixir-mode)
446+
(if (elixir-smie--heredoc-at-current-point-p)
447+
(let ((indent
448+
(save-excursion
449+
(when (re-search-backward "^\\(\s+\\).+\"\"\"" nil t)
450+
(string-width (match-string 1))))))
451+
(if (elixir-smie--previous-line-empty-p)
452+
(goto-char indent)
453+
(goto-char (elixir-smie--previous-line-indentation)))))))
454+
455+
(add-to-list 'smie-indent-functions 'elixir-smie--indent-inside-heredoc)
456+
410457
(provide 'elixir-smie)
411458

412459
;;; elixir-smie.el ends here

test/elixir-mode-helper-test.el

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
;;; elixir-mode-helper-test.el --- Tests for helper functions
2+
3+
;;; Code:
4+
5+
(ert-deftest check-if-currently-inside-heredoc ()
6+
(should (with-temp-buffer
7+
(elixir-mode)
8+
(insert "
9+
defmodule Module.Name do
10+
11+
@moduledoc \"\"\"
12+
## Examples
13+
14+
....
15+
\"\"\"
16+
17+
end")
18+
(goto-line 7)
19+
(elixir-smie--heredoc-at-current-point-p)))
20+
(should (not (with-temp-buffer
21+
(elixir-mode)
22+
(insert "
23+
defmodule Module.Name do
24+
25+
@moduledoc \"\"\"
26+
## Examples
27+
28+
....
29+
\"\"\"
30+
31+
end")
32+
(goto-line 3)
33+
(elixir-smie--heredoc-at-current-point-p)))))
34+
35+
(ert-deftest get-previous-line-indentation ()
36+
(should (equal 2
37+
(with-temp-buffer
38+
(elixir-mode)
39+
(insert "
40+
defmodule Module.Name do
41+
def what do
42+
1 + 1
43+
end
44+
end")
45+
(goto-line 4)
46+
(elixir-smie--previous-line-indentation))))
47+
)
48+
49+
50+
(ert-deftest check-if-previous-line-blank ()
51+
(should (not (with-temp-buffer
52+
(elixir-mode)
53+
(insert "
54+
defmodule Module.Name do
55+
56+
def what do
57+
1 + 1
58+
end
59+
end")
60+
(goto-line 3)
61+
(elixir-smie--previous-line-empty-p))))
62+
(should (with-temp-buffer
63+
(elixir-mode)
64+
(insert "
65+
defmodule Module.Name do
66+
67+
68+
def what do
69+
1 + 1
70+
end
71+
end")
72+
(goto-line 4)
73+
(elixir-smie--previous-line-empty-p))))
74+
75+
(provide 'elixir-mode-helper-test)
76+
77+
;;; elixir-mode-helper-test.el ends here

test/elixir-mode-indentation-test.el

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,32 @@ end
601601
")
602602

603603
(elixir-def-indentation-test indent-heredoc
604-
(:expected-result :failed :tags '(indentation))
604+
(:tags '(indentation))
605+
"
606+
defmodule Foo do
607+
@doc \"\"\"
608+
this is a heredoc string
609+
610+
\"\"\"
611+
def convert do
612+
x = 15
613+
end
614+
end
615+
"
616+
"
617+
defmodule Foo do
618+
@doc \"\"\"
619+
this is a heredoc string
620+
621+
\"\"\"
622+
def convert do
623+
x = 15
624+
end
625+
end
626+
")
627+
628+
(elixir-def-indentation-test indent-heredoc/2
629+
(:tags '(indentation))
605630
"
606631
defmodule Foo do
607632
@doc \"\"\"
@@ -611,6 +636,14 @@ this is a heredoc string
611636
def convert do
612637
x = 15
613638
end
639+
640+
defmodule Bar do
641+
@moduledoc \"\"\"
642+
this is a heredoc string
643+
644+
last line
645+
\"\"\"
646+
end
614647
end
615648
"
616649
"
@@ -622,6 +655,14 @@ defmodule Foo do
622655
def convert do
623656
x = 15
624657
end
658+
659+
defmodule Bar do
660+
@moduledoc \"\"\"
661+
this is a heredoc string
662+
663+
last line
664+
\"\"\"
665+
end
625666
end
626667
")
627668

0 commit comments

Comments
 (0)