Skip to content

Commit 21e2baf

Browse files
committed
correct indentation inside heredoc strings
1 parent ea42d33 commit 21e2baf

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
@@ -398,6 +398,53 @@
398398
(smie-rule-hanging-p)))
399399
(smie-rule-parent elixir-smie-indent-basic))))))
400400

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

403450
;;; 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)